Refactor Rust timeline identifiers into our own. (#3731)

* Refactor eventOrTransactionID.

* Refactor uniqueID.
This commit is contained in:
Doug
2025-02-04 09:50:46 +00:00
committed by GitHub
parent 54dbb33ad5
commit bad4a8f9c8
39 changed files with 196 additions and 175 deletions
@@ -10,9 +10,9 @@ import Foundation
enum RoomTimelineItemFixtures {
/// The default timeline items used in Xcode previews etc.
static var `default`: [RoomTimelineItemProtocol] = [
SeparatorRoomTimelineItem(id: .virtual(uniqueID: .init(id: "Yesterday")), timestamp: .mock),
TextRoomTimelineItem(id: .event(uniqueID: .init(id: ".RoomTimelineItemFixtures.default.0"),
eventOrTransactionID: .eventId(eventId: "RoomTimelineItemFixtures.default.0")),
SeparatorRoomTimelineItem(id: .virtual(uniqueID: .init("Yesterday")), timestamp: .mock),
TextRoomTimelineItem(id: .event(uniqueID: .init(".RoomTimelineItemFixtures.default.0"),
eventOrTransactionID: .eventID("RoomTimelineItemFixtures.default.0")),
timestamp: .mock,
isOutgoing: false,
isEditable: false,
@@ -21,8 +21,8 @@ enum RoomTimelineItemFixtures {
sender: .init(id: "", displayName: "Jacob"),
content: .init(body: "That looks so good!"),
properties: RoomTimelineItemProperties(isEdited: true)),
TextRoomTimelineItem(id: .event(uniqueID: .init(id: "RoomTimelineItemFixtures.default.1"),
eventOrTransactionID: .eventId(eventId: "RoomTimelineItemFixtures.default.1")),
TextRoomTimelineItem(id: .event(uniqueID: .init("RoomTimelineItemFixtures.default.1"),
eventOrTransactionID: .eventID("RoomTimelineItemFixtures.default.1")),
timestamp: .mock,
isOutgoing: false,
isEditable: false,
@@ -33,8 +33,8 @@ enum RoomTimelineItemFixtures {
properties: RoomTimelineItemProperties(reactions: [
AggregatedReaction(accountOwnerID: "me", key: "🙌", senders: [ReactionSender(id: "me", timestamp: Date())])
])),
TextRoomTimelineItem(id: .event(uniqueID: .init(id: "RoomTimelineItemFixtures.default.2"),
eventOrTransactionID: .eventId(eventId: "RoomTimelineItemFixtures.default.2")),
TextRoomTimelineItem(id: .event(uniqueID: .init("RoomTimelineItemFixtures.default.2"),
eventOrTransactionID: .eventID("RoomTimelineItemFixtures.default.2")),
timestamp: .mock,
isOutgoing: false,
isEditable: false,
@@ -52,9 +52,9 @@ enum RoomTimelineItemFixtures {
ReactionSender(id: "jacob", timestamp: Date())
])
])),
SeparatorRoomTimelineItem(id: .virtual(uniqueID: .init(id: "Today")), timestamp: .mock),
TextRoomTimelineItem(id: .event(uniqueID: .init(id: "RoomTimelineItemFixtures.default.3"),
eventOrTransactionID: .eventId(eventId: "RoomTimelineItemFixtures.default.3")),
SeparatorRoomTimelineItem(id: .virtual(uniqueID: .init("Today")), timestamp: .mock),
TextRoomTimelineItem(id: .event(uniqueID: .init("RoomTimelineItemFixtures.default.3"),
eventOrTransactionID: .eventID("RoomTimelineItemFixtures.default.3")),
timestamp: .mock,
isOutgoing: false,
isEditable: false,
@@ -63,8 +63,8 @@ enum RoomTimelineItemFixtures {
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: .event(uniqueID: .init(id: "RoomTimelineItemFixtures.default.4"),
eventOrTransactionID: .eventId(eventId: "RoomTimelineItemFixtures.default.4")),
TextRoomTimelineItem(id: .event(uniqueID: .init("RoomTimelineItemFixtures.default.4"),
eventOrTransactionID: .eventID("RoomTimelineItemFixtures.default.4")),
timestamp: .mock,
isOutgoing: true,
isEditable: true,
@@ -72,8 +72,8 @@ enum RoomTimelineItemFixtures {
isThreaded: false,
sender: .init(id: "", displayName: "Bob"),
content: .init(body: "And John's speech was amazing!")),
TextRoomTimelineItem(id: .event(uniqueID: .init(id: "RoomTimelineItemFixtures.default.5"),
eventOrTransactionID: .eventId(eventId: "RoomTimelineItemFixtures.default.5")),
TextRoomTimelineItem(id: .event(uniqueID: .init("RoomTimelineItemFixtures.default.5"),
eventOrTransactionID: .eventID("RoomTimelineItemFixtures.default.5")),
timestamp: .mock,
isOutgoing: true,
isEditable: true,
@@ -86,8 +86,8 @@ enum RoomTimelineItemFixtures {
ReadReceipt(userID: "bob", formattedTimestamp: nil),
ReadReceipt(userID: "charlie", formattedTimestamp: nil),
ReadReceipt(userID: "dan", formattedTimestamp: nil)])),
TextRoomTimelineItem(id: .event(uniqueID: .init(id: "RoomTimelineItemFixtures.default.6"),
eventOrTransactionID: .eventId(eventId: "RoomTimelineItemFixtures.default.6")),
TextRoomTimelineItem(id: .event(uniqueID: .init("RoomTimelineItemFixtures.default.6"),
eventOrTransactionID: .eventID("RoomTimelineItemFixtures.default.6")),
timestamp: .mock,
isOutgoing: false,
isEditable: false,
@@ -242,7 +242,7 @@ enum RoomTimelineItemFixtures {
static var permalinkChunk: [RoomTimelineItemProtocol] {
(1...20).map { index in
TextRoomTimelineItem(id: .event(uniqueID: .init(id: "\(index)"), eventOrTransactionID: .eventId(eventId: "$\(index)")),
TextRoomTimelineItem(id: .event(uniqueID: .init("\(index)"), eventOrTransactionID: .eventID("$\(index)")),
text: "Message ID \(index)",
senderDisplayName: index > 10 ? "Alice" : "Bob")
}
@@ -264,7 +264,7 @@ enum RoomTimelineItemFixtures {
}
static var separator: SeparatorRoomTimelineItem {
SeparatorRoomTimelineItem(id: .virtual(uniqueID: .init(id: UUID().uuidString)),
SeparatorRoomTimelineItem(id: .virtual(uniqueID: .init(UUID().uuidString)),
timestamp: .now)
}
}
@@ -104,22 +104,22 @@ class MockTimelineController: TimelineControllerProtocol {
inReplyToEventID: String?,
intentionalMentions: IntentionalMentions) async { }
func toggleReaction(_ reaction: String, to eventID: EventOrTransactionId) async { }
func toggleReaction(_ reaction: String, to eventID: TimelineItemIdentifier.EventOrTransactionID) async { }
func edit(_ eventOrTransactionID: EventOrTransactionId,
func edit(_ eventOrTransactionID: TimelineItemIdentifier.EventOrTransactionID,
message: String,
html: String?,
intentionalMentions: IntentionalMentions) async { }
func editCaption(_ eventOrTransactionID: EventOrTransactionId,
func editCaption(_ eventOrTransactionID: TimelineItemIdentifier.EventOrTransactionID,
message: String,
html: String?,
intentionalMentions: IntentionalMentions) async { }
func removeCaption(_ eventOrTransactionID: EventOrTransactionId) async { }
func removeCaption(_ eventOrTransactionID: TimelineItemIdentifier.EventOrTransactionID) async { }
private(set) var redactCalled = false
func redact(_ eventOrTransactionID: EventOrTransactionId) async {
func redact(_ eventOrTransactionID: TimelineItemIdentifier.EventOrTransactionID) async {
redactCalled = true
}
@@ -221,7 +221,7 @@ class TimelineController: TimelineControllerProtocol {
}
}
func toggleReaction(_ reaction: String, to eventOrTransactionID: EventOrTransactionId) async {
func toggleReaction(_ reaction: String, to eventOrTransactionID: TimelineItemIdentifier.EventOrTransactionID) async {
MXLog.info("Toggle reaction \(reaction) to \(eventOrTransactionID)")
switch await activeTimeline.toggleReaction(reaction, to: eventOrTransactionID) {
@@ -232,7 +232,7 @@ class TimelineController: TimelineControllerProtocol {
}
}
func edit(_ eventOrTransactionID: EventOrTransactionId,
func edit(_ eventOrTransactionID: TimelineItemIdentifier.EventOrTransactionID,
message: String,
html: String?,
intentionalMentions: IntentionalMentions) async {
@@ -251,7 +251,7 @@ class TimelineController: TimelineControllerProtocol {
}
}
func editCaption(_ eventOrTransactionID: EventOrTransactionId,
func editCaption(_ eventOrTransactionID: TimelineItemIdentifier.EventOrTransactionID,
message: String,
html: String?,
intentionalMentions: IntentionalMentions) async {
@@ -269,7 +269,7 @@ class TimelineController: TimelineControllerProtocol {
}
}
func removeCaption(_ eventOrTransactionID: EventOrTransactionId) async {
func removeCaption(_ eventOrTransactionID: TimelineItemIdentifier.EventOrTransactionID) async {
// Set a `nil` caption to remove it from the event.
let newContent = createCaptionEdit(caption: nil, formattedCaption: nil, mentions: nil)
switch await activeTimeline.edit(eventOrTransactionID, newContent: newContent) {
@@ -280,7 +280,7 @@ class TimelineController: TimelineControllerProtocol {
}
}
func redact(_ eventOrTransactionID: EventOrTransactionId) async {
func redact(_ eventOrTransactionID: TimelineItemIdentifier.EventOrTransactionID) async {
MXLog.info("Send redaction in \(roomID)")
switch await activeTimeline.redact(eventOrTransactionID, reason: nil) {
@@ -56,21 +56,21 @@ protocol TimelineControllerProtocol {
inReplyToEventID: String?,
intentionalMentions: IntentionalMentions) async
func edit(_ eventOrTransactionID: EventOrTransactionId,
func edit(_ eventOrTransactionID: TimelineItemIdentifier.EventOrTransactionID,
message: String,
html: String?,
intentionalMentions: IntentionalMentions) async
func editCaption(_ eventOrTransactionID: EventOrTransactionId,
func editCaption(_ eventOrTransactionID: TimelineItemIdentifier.EventOrTransactionID,
message: String,
html: String?,
intentionalMentions: IntentionalMentions) async
func removeCaption(_ eventOrTransactionID: EventOrTransactionId) async
func removeCaption(_ eventOrTransactionID: TimelineItemIdentifier.EventOrTransactionID) async
func toggleReaction(_ reaction: String, to eventOrTransactionID: EventOrTransactionId) async
func toggleReaction(_ reaction: String, to eventOrTransactionID: TimelineItemIdentifier.EventOrTransactionID) async
func redact(_ eventOrTransactionID: EventOrTransactionId) async
func redact(_ eventOrTransactionID: TimelineItemIdentifier.EventOrTransactionID) async
func pin(eventID: String) async
@@ -6,7 +6,7 @@
//
import Foundation
import MatrixRustSDK
@preconcurrency import MatrixRustSDK
/// A timeline item identifier
/// - uniqueID: Stable id across state changes of the timeline item, it uniquely identifies an item in a timeline.
@@ -14,42 +14,63 @@ import MatrixRustSDK
/// - eventOrTransactionID: Contains the 2 possible identifiers of an event, either it has a remote event id or
/// a local transaction id, never both or none.
enum TimelineItemIdentifier: Hashable, Sendable {
case event(uniqueID: TimelineUniqueId, eventOrTransactionID: EventOrTransactionId)
case virtual(uniqueID: TimelineUniqueId)
var uniqueID: TimelineUniqueId {
switch self {
case .event(let uniqueID, _):
return uniqueID
case .virtual(let uniqueID):
return uniqueID
struct UniqueID: Hashable {
let value: String
init(_ value: String) {
self.value = value
}
init(rustValue: TimelineUniqueId) {
self.init(rustValue.id)
}
var rustValue: TimelineUniqueId {
.init(id: value)
}
}
enum EventOrTransactionID: Hashable {
case eventID(String), transactionID(String)
init(rustValue: EventOrTransactionId) {
switch rustValue {
case .eventId(let eventID): self = .eventID(eventID)
case .transactionId(let transactionID): self = .transactionID(transactionID)
}
}
var rustValue: EventOrTransactionId {
switch self {
case .eventID(let eventID): .eventId(eventId: eventID)
case .transactionID(let transactionID): .transactionId(transactionId: transactionID)
}
}
}
case event(uniqueID: UniqueID, eventOrTransactionID: EventOrTransactionID)
case virtual(uniqueID: UniqueID)
var uniqueID: UniqueID {
switch self {
case .event(let uniqueID, _): uniqueID
case .virtual(let uniqueID): uniqueID
}
}
var eventOrTransactionID: EventOrTransactionID? {
guard case let .event(_, eventOrTransactionID) = self else { return nil }
return eventOrTransactionID
}
var eventID: String? {
guard case let .event(_, eventOrTransactionID) = self else {
return nil
}
switch eventOrTransactionID {
case .eventId(let eventID):
return eventID
default:
return nil
}
guard case let .event(_, .eventID(eventID)) = self else { return nil }
return eventID
}
var transactionID: String? {
guard case let .event(_, eventOrTransactionID) = self else {
return nil
}
switch eventOrTransactionID {
case .transactionId(let transactionID):
return transactionID
default:
return nil
}
guard case let .event(_, .transactionID(transactionID)) = self else { return nil }
return transactionID
}
}
@@ -57,10 +78,10 @@ enum TimelineItemIdentifier: Hashable, Sendable {
extension TimelineItemIdentifier {
static var randomEvent: Self {
.event(uniqueID: .init(id: UUID().uuidString), eventOrTransactionID: .eventId(eventId: UUID().uuidString))
.event(uniqueID: .init(UUID().uuidString), eventOrTransactionID: .eventID(UUID().uuidString))
}
static var randomVirtual: Self {
.virtual(uniqueID: .init(id: UUID().uuidString))
.virtual(uniqueID: .init(UUID().uuidString))
}
}
@@ -11,14 +11,14 @@ import MatrixRustSDK
/// A light wrapper around timeline items returned from Rust.
enum TimelineItemProxy {
case event(EventTimelineItemProxy)
case virtual(MatrixRustSDK.VirtualTimelineItem, uniqueID: TimelineUniqueId)
case virtual(MatrixRustSDK.VirtualTimelineItem, uniqueID: TimelineItemIdentifier.UniqueID)
case unknown(MatrixRustSDK.TimelineItem)
init(item: MatrixRustSDK.TimelineItem) {
if let eventItem = item.asEvent() {
self = .event(EventTimelineItemProxy(item: eventItem, uniqueID: item.uniqueId()))
self = .event(EventTimelineItemProxy(item: eventItem, uniqueID: .init(rustValue: item.uniqueId())))
} else if let virtualItem = item.asVirtual() {
self = .virtual(virtualItem, uniqueID: item.uniqueId())
self = .virtual(virtualItem, uniqueID: .init(rustValue: item.uniqueId()))
} else {
self = .unknown(item)
}
@@ -71,10 +71,10 @@ class EventTimelineItemProxy {
let item: MatrixRustSDK.EventTimelineItem
let id: TimelineItemIdentifier
init(item: MatrixRustSDK.EventTimelineItem, uniqueID: TimelineUniqueId) {
init(item: MatrixRustSDK.EventTimelineItem, uniqueID: TimelineItemIdentifier.UniqueID) {
self.item = item
id = .event(uniqueID: uniqueID, eventOrTransactionID: item.eventOrTransactionId)
id = .event(uniqueID: uniqueID, eventOrTransactionID: .init(rustValue: item.eventOrTransactionId))
}
lazy var deliveryStatus: TimelineItemDeliveryStatus? = {
@@ -211,8 +211,8 @@ struct SendHandleProxy: Hashable {
}
static var mock: SendHandleProxy {
.init(itemID: .event(uniqueID: .init(id: UUID().uuidString),
eventOrTransactionID: .eventId(eventId: UUID().uuidString)),
.init(itemID: .event(uniqueID: .init(UUID().uuidString),
eventOrTransactionID: .eventID(UUID().uuidString)),
underlyingHandle: .init(noPointer: .init()))
}
}
@@ -22,6 +22,6 @@ struct PaginationIndicatorRoomTimelineItem: DecorationTimelineItemProtocol, Equa
}
init(position: Position) {
id = .virtual(uniqueID: .init(id: position.id))
id = .virtual(uniqueID: .init(position.id))
}
}
@@ -8,6 +8,6 @@
import Foundation
struct TimelineStartRoomTimelineItem: DecorationTimelineItemProtocol, Equatable {
let id: TimelineItemIdentifier = .virtual(uniqueID: .init(id: UUID().uuidString))
let id: TimelineItemIdentifier = .virtual(uniqueID: .init(UUID().uuidString))
let name: String?
}
@@ -35,7 +35,7 @@ final class RoomTimelineItemViewState: Identifiable, Equatable, ObservableObject
// MARK: Identifiable
/// The `timelineID` of the item, used for the timeline view level identification, do not use for any business logic use `identifier` instead
var id: TimelineUniqueId {
var id: TimelineItemIdentifier.UniqueID {
identifier.uniqueID
}
}
@@ -166,9 +166,9 @@ final class TimelineProxy: TimelineProxyProtocol {
}
}
func edit(_ eventOrTransactionID: EventOrTransactionId, newContent: EditedContent) async -> Result<Void, TimelineProxyError> {
func edit(_ eventOrTransactionID: TimelineItemIdentifier.EventOrTransactionID, newContent: EditedContent) async -> Result<Void, TimelineProxyError> {
do {
try await timeline.edit(eventOrTransactionId: eventOrTransactionID, newContent: newContent)
try await timeline.edit(eventOrTransactionId: eventOrTransactionID.rustValue, newContent: newContent)
MXLog.info("Finished editing timeline item: \(eventOrTransactionID)")
@@ -179,11 +179,11 @@ final class TimelineProxy: TimelineProxyProtocol {
}
}
func redact(_ eventOrTransactionID: EventOrTransactionId, reason: String?) async -> Result<Void, TimelineProxyError> {
func redact(_ eventOrTransactionID: TimelineItemIdentifier.EventOrTransactionID, reason: String?) async -> Result<Void, TimelineProxyError> {
MXLog.info("Redacting timeline item: \(eventOrTransactionID)")
do {
try await timeline.redactEvent(eventOrTransactionId: eventOrTransactionID, reason: reason)
try await timeline.redactEvent(eventOrTransactionId: eventOrTransactionID.rustValue, reason: reason)
MXLog.info("Redacted timeline item: \(eventOrTransactionID)")
@@ -443,11 +443,11 @@ final class TimelineProxy: TimelineProxyProtocol {
}
}
func toggleReaction(_ reaction: String, to eventOrTransactionID: EventOrTransactionId) async -> Result<Void, TimelineProxyError> {
func toggleReaction(_ reaction: String, to eventOrTransactionID: TimelineItemIdentifier.EventOrTransactionID) async -> Result<Void, TimelineProxyError> {
MXLog.info("Toggling reaction \(reaction) for event: \(eventOrTransactionID)")
do {
try await timeline.toggleReaction(itemId: eventOrTransactionID, key: reaction)
try await timeline.toggleReaction(itemId: eventOrTransactionID.rustValue, key: reaction)
MXLog.info("Finished toggling reaction for event: \(eventOrTransactionID)")
return .success(())
} catch {
@@ -650,7 +650,7 @@ extension Array where Element == TimelineItemProxy {
return nil
}
func firstEventTimelineItemUsingEventOrTransactionID(_ eventOrTransactionID: EventOrTransactionId) -> EventTimelineItem? {
func firstEventTimelineItemUsingEventOrTransactionID(_ eventOrTransactionID: TimelineItemIdentifier.EventOrTransactionID) -> EventTimelineItem? {
for item in self {
if case let .event(eventTimelineItem) = item,
case let .event(_, identifier) = eventTimelineItem.id,
@@ -40,10 +40,10 @@ protocol TimelineProxyProtocol {
func paginateBackwards(requestSize: UInt16) async -> Result<Void, TimelineProxyError>
func paginateForwards(requestSize: UInt16) async -> Result<Void, TimelineProxyError>
func edit(_ eventOrTransactionID: EventOrTransactionId,
func edit(_ eventOrTransactionID: TimelineItemIdentifier.EventOrTransactionID,
newContent: EditedContent) async -> Result<Void, TimelineProxyError>
func redact(_ eventOrTransactionID: EventOrTransactionId,
func redact(_ eventOrTransactionID: TimelineItemIdentifier.EventOrTransactionID,
reason: String?) async -> Result<Void, TimelineProxyError>
func pin(eventID: String) async -> Result<Bool, TimelineProxyError>
@@ -94,7 +94,7 @@ protocol TimelineProxyProtocol {
inReplyToEventID: String?,
intentionalMentions: IntentionalMentions) async -> Result<Void, TimelineProxyError>
func toggleReaction(_ reaction: String, to eventID: EventOrTransactionId) async -> Result<Void, TimelineProxyError>
func toggleReaction(_ reaction: String, to eventID: TimelineItemIdentifier.EventOrTransactionID) async -> Result<Void, TimelineProxyError>
func createPoll(question: String, answers: [String], pollKind: Poll.Kind) async -> Result<Void, TimelineProxyError>