diff --git a/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift b/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift index 90e9e2e29..dc90cec10 100644 --- a/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift +++ b/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift @@ -14944,8 +14944,8 @@ class TimelineProxyMock: TimelineProxyProtocol, @unchecked Sendable { var editNewContentCalled: Bool { return editNewContentCallsCount > 0 } - var editNewContentReceivedArguments: (eventOrTransactionID: EventOrTransactionId, newContent: EditedContent)? - var editNewContentReceivedInvocations: [(eventOrTransactionID: EventOrTransactionId, newContent: EditedContent)] = [] + var editNewContentReceivedArguments: (eventOrTransactionID: TimelineItemIdentifier.EventOrTransactionID, newContent: EditedContent)? + var editNewContentReceivedInvocations: [(eventOrTransactionID: TimelineItemIdentifier.EventOrTransactionID, newContent: EditedContent)] = [] var editNewContentUnderlyingReturnValue: Result! var editNewContentReturnValue: Result! { @@ -14971,9 +14971,9 @@ class TimelineProxyMock: TimelineProxyProtocol, @unchecked Sendable { } } } - var editNewContentClosure: ((EventOrTransactionId, EditedContent) async -> Result)? + var editNewContentClosure: ((TimelineItemIdentifier.EventOrTransactionID, EditedContent) async -> Result)? - func edit(_ eventOrTransactionID: EventOrTransactionId, newContent: EditedContent) async -> Result { + func edit(_ eventOrTransactionID: TimelineItemIdentifier.EventOrTransactionID, newContent: EditedContent) async -> Result { editNewContentCallsCount += 1 editNewContentReceivedArguments = (eventOrTransactionID: eventOrTransactionID, newContent: newContent) DispatchQueue.main.async { @@ -15014,8 +15014,8 @@ class TimelineProxyMock: TimelineProxyProtocol, @unchecked Sendable { var redactReasonCalled: Bool { return redactReasonCallsCount > 0 } - var redactReasonReceivedArguments: (eventOrTransactionID: EventOrTransactionId, reason: String?)? - var redactReasonReceivedInvocations: [(eventOrTransactionID: EventOrTransactionId, reason: String?)] = [] + var redactReasonReceivedArguments: (eventOrTransactionID: TimelineItemIdentifier.EventOrTransactionID, reason: String?)? + var redactReasonReceivedInvocations: [(eventOrTransactionID: TimelineItemIdentifier.EventOrTransactionID, reason: String?)] = [] var redactReasonUnderlyingReturnValue: Result! var redactReasonReturnValue: Result! { @@ -15041,9 +15041,9 @@ class TimelineProxyMock: TimelineProxyProtocol, @unchecked Sendable { } } } - var redactReasonClosure: ((EventOrTransactionId, String?) async -> Result)? + var redactReasonClosure: ((TimelineItemIdentifier.EventOrTransactionID, String?) async -> Result)? - func redact(_ eventOrTransactionID: EventOrTransactionId, reason: String?) async -> Result { + func redact(_ eventOrTransactionID: TimelineItemIdentifier.EventOrTransactionID, reason: String?) async -> Result { redactReasonCallsCount += 1 redactReasonReceivedArguments = (eventOrTransactionID: eventOrTransactionID, reason: reason) DispatchQueue.main.async { @@ -15824,8 +15824,8 @@ class TimelineProxyMock: TimelineProxyProtocol, @unchecked Sendable { var toggleReactionToCalled: Bool { return toggleReactionToCallsCount > 0 } - var toggleReactionToReceivedArguments: (reaction: String, eventID: EventOrTransactionId)? - var toggleReactionToReceivedInvocations: [(reaction: String, eventID: EventOrTransactionId)] = [] + var toggleReactionToReceivedArguments: (reaction: String, eventID: TimelineItemIdentifier.EventOrTransactionID)? + var toggleReactionToReceivedInvocations: [(reaction: String, eventID: TimelineItemIdentifier.EventOrTransactionID)] = [] var toggleReactionToUnderlyingReturnValue: Result! var toggleReactionToReturnValue: Result! { @@ -15851,9 +15851,9 @@ class TimelineProxyMock: TimelineProxyProtocol, @unchecked Sendable { } } } - var toggleReactionToClosure: ((String, EventOrTransactionId) async -> Result)? + var toggleReactionToClosure: ((String, TimelineItemIdentifier.EventOrTransactionID) async -> Result)? - func toggleReaction(_ reaction: String, to eventID: EventOrTransactionId) async -> Result { + func toggleReaction(_ reaction: String, to eventID: TimelineItemIdentifier.EventOrTransactionID) async -> Result { toggleReactionToCallsCount += 1 toggleReactionToReceivedArguments = (reaction: reaction, eventID: eventID) DispatchQueue.main.async { diff --git a/ElementX/Sources/Other/SwiftUI/Views/LoadableImage.swift b/ElementX/Sources/Other/SwiftUI/Views/LoadableImage.swift index 547db6709..0d46166f5 100644 --- a/ElementX/Sources/Other/SwiftUI/Views/LoadableImage.swift +++ b/ElementX/Sources/Other/SwiftUI/Views/LoadableImage.swift @@ -15,7 +15,7 @@ enum LoadableImageMediaType: Equatable { /// An avatar (can be displayed anywhere within the app). case avatar /// An image displayed in the timeline. - case timelineItem(uniqueID: String) + case timelineItem(uniqueID: TimelineItemIdentifier.UniqueID) /// Any other media (can be displayed anywhere within the app). case generic } @@ -88,7 +88,7 @@ struct LoadableImage: View { switch mediaType { case .timelineItem(let uniqueID): // Consider media for the same item to be the same view - uniqueID + uniqueID.value default: // Binds the lifecycle of the LoadableImage to the associated URL. // This fixes the problem of the cache returning old values after a change in the URL. @@ -323,33 +323,33 @@ struct LoadableImage_Previews: PreviewProvider, TestablePreview { static var previews: some View { LazyVGrid(columns: [.init(.adaptive(minimum: 110, maximum: 110))], spacing: 24) { LoadableImage(url: "mxc://wherever/1234", - mediaType: .timelineItem(uniqueID: "id"), + mediaType: .timelineItem(uniqueID: .init("id")), mediaProvider: mediaProvider, placeholder: placeholder) .layout(title: "Loaded") LoadableImage(url: "mxc://wherever/2345", - mediaType: .timelineItem(uniqueID: "id"), + mediaType: .timelineItem(uniqueID: .init("id")), blurhash: "KpE4oyayR5|GbHb];3j@of", mediaProvider: mediaProvider, placeholder: placeholder) .layout(title: "Hidden (blurhash)", hideTimelineMedia: true) LoadableImage(url: "mxc://wherever/3456", - mediaType: .timelineItem(uniqueID: "id"), + mediaType: .timelineItem(uniqueID: .init("id")), mediaProvider: mediaProvider, placeholder: placeholder) .layout(title: "Hidden (placeholder)", hideTimelineMedia: true) LoadableImage(url: "mxc://wherever/4567", - mediaType: .timelineItem(uniqueID: "id"), + mediaType: .timelineItem(uniqueID: .init("id")), blurhash: "KbLM^j]q$jT|EfR-3rtjXk", mediaProvider: loadingMediaProvider, placeholder: placeholder) .layout(title: "Loading (blurhash)") LoadableImage(url: "mxc://wherever/5678", - mediaType: .timelineItem(uniqueID: "id"), + mediaType: .timelineItem(uniqueID: .init("id")), mediaProvider: loadingMediaProvider, placeholder: placeholder) .layout(title: "Loading (placeholder)") @@ -361,7 +361,7 @@ struct LoadableImage_Previews: PreviewProvider, TestablePreview { .layout(title: "Loading (avatar)") LoadableImage(url: "mxc://wherever/345", - mediaType: .timelineItem(uniqueID: "id"), + mediaType: .timelineItem(uniqueID: .init("id")), blurhash: "KbLM^j]q$jT|EfR-3rtjXk", mediaProvider: mediaProvider, transformer: transformer, @@ -369,7 +369,7 @@ struct LoadableImage_Previews: PreviewProvider, TestablePreview { .layout(title: "Loaded (transformer)") LoadableImage(url: "mxc://wherever/345", - mediaType: .timelineItem(uniqueID: "id"), + mediaType: .timelineItem(uniqueID: .init("id")), blurhash: "KbLM^j]q$jT|EfR-3rtjXk", mediaProvider: loadingMediaProvider, transformer: transformer, @@ -377,7 +377,7 @@ struct LoadableImage_Previews: PreviewProvider, TestablePreview { .layout(title: "Loading (transformer)") LoadableImage(url: "mxc://wherever/234", - mediaType: .timelineItem(uniqueID: "id"), + mediaType: .timelineItem(uniqueID: .init("id")), blurhash: "KbLM^j]q$jT|EfR-3rtjXk", mediaProvider: mediaProvider, transformer: transformer, diff --git a/ElementX/Sources/Screens/FilePreviewScreen/View/TimelineMediaPreviewRedactConfirmationView.swift b/ElementX/Sources/Screens/FilePreviewScreen/View/TimelineMediaPreviewRedactConfirmationView.swift index 94688d8d2..194f79368 100644 --- a/ElementX/Sources/Screens/FilePreviewScreen/View/TimelineMediaPreviewRedactConfirmationView.swift +++ b/ElementX/Sources/Screens/FilePreviewScreen/View/TimelineMediaPreviewRedactConfirmationView.swift @@ -63,7 +63,7 @@ struct TimelineMediaPreviewRedactConfirmationView: View { .scaledFrame(size: 40) .background { LoadableImage(mediaSource: mediaSource, - mediaType: .timelineItem(uniqueID: item.id.uniqueID.id), + mediaType: .timelineItem(uniqueID: item.id.uniqueID), blurhash: item.blurhash, mediaProvider: context.mediaProvider) { Color.compound.bgSubtleSecondary diff --git a/ElementX/Sources/Screens/MediaEventsTimelineScreen/MediaEventsTimelineScreenViewModel.swift b/ElementX/Sources/Screens/MediaEventsTimelineScreen/MediaEventsTimelineScreenViewModel.swift index efea757f6..57d1dd1d2 100644 --- a/ElementX/Sources/Screens/MediaEventsTimelineScreen/MediaEventsTimelineScreenViewModel.swift +++ b/ElementX/Sources/Screens/MediaEventsTimelineScreen/MediaEventsTimelineScreenViewModel.swift @@ -113,7 +113,7 @@ class MediaEventsTimelineScreenViewModel: MediaEventsTimelineScreenViewModelType } }.reversed().forEach { item in if case .separator(let item) = item.type { - let group = MediaEventsTimelineGroup(id: item.id.uniqueID.id, + let group = MediaEventsTimelineGroup(id: item.id.uniqueID.value, title: titleForDate(item.timestamp), items: currentItems) if !currentItems.isEmpty { diff --git a/ElementX/Sources/Screens/MediaEventsTimelineScreen/View/TimelineViews/ImageMediaEventsTimelineView.swift b/ElementX/Sources/Screens/MediaEventsTimelineScreen/View/TimelineViews/ImageMediaEventsTimelineView.swift index 041ffb0c9..47472a2bb 100644 --- a/ElementX/Sources/Screens/MediaEventsTimelineScreen/View/TimelineViews/ImageMediaEventsTimelineView.swift +++ b/ElementX/Sources/Screens/MediaEventsTimelineScreen/View/TimelineViews/ImageMediaEventsTimelineView.swift @@ -27,7 +27,7 @@ struct ImageMediaEventsTimelineView: View { private var loadableImage: some View { if timelineItem.content.contentType == .gif { LoadableImage(mediaSource: timelineItem.content.imageInfo.source, - mediaType: .timelineItem(uniqueID: timelineItem.id.uniqueID.id), + mediaType: .timelineItem(uniqueID: timelineItem.id.uniqueID), blurhash: timelineItem.content.blurhash, size: timelineItem.content.imageInfo.size, mediaProvider: context?.mediaProvider) { @@ -36,7 +36,7 @@ struct ImageMediaEventsTimelineView: View { .mediaGalleryTimelineAspectRatio(imageInfo: timelineItem.content.imageInfo) } else { LoadableImage(mediaSource: timelineItem.content.thumbnailInfo?.source ?? timelineItem.content.imageInfo.source, - mediaType: .timelineItem(uniqueID: timelineItem.id.uniqueID.id), + mediaType: .timelineItem(uniqueID: timelineItem.id.uniqueID), blurhash: timelineItem.content.blurhash, size: timelineItem.content.thumbnailInfo?.size ?? timelineItem.content.imageInfo.size, mediaProvider: context?.mediaProvider) { diff --git a/ElementX/Sources/Screens/MediaEventsTimelineScreen/View/TimelineViews/SeparatorMediaEventsTimelineView.swift b/ElementX/Sources/Screens/MediaEventsTimelineScreen/View/TimelineViews/SeparatorMediaEventsTimelineView.swift index d9d245b7a..fbb4a0079 100644 --- a/ElementX/Sources/Screens/MediaEventsTimelineScreen/View/TimelineViews/SeparatorMediaEventsTimelineView.swift +++ b/ElementX/Sources/Screens/MediaEventsTimelineScreen/View/TimelineViews/SeparatorMediaEventsTimelineView.swift @@ -22,10 +22,10 @@ struct SeparatorMediaEventsTimelineView: View { struct SeparatorMediaEventsTimelineView_Previews: PreviewProvider, TestablePreview { static var previews: some View { - let item = SeparatorRoomTimelineItem(id: .virtual(uniqueID: .init(id: "Separator")), + let item = SeparatorRoomTimelineItem(id: .virtual(uniqueID: .init("Separator")), timestamp: .mock) - SeparatorMediaEventsTimelineView(group: .init(id: item.id.uniqueID.id, + SeparatorMediaEventsTimelineView(group: .init(id: item.id.uniqueID.value, title: "Group", items: [])) } diff --git a/ElementX/Sources/Screens/MediaEventsTimelineScreen/View/TimelineViews/VideoMediaEventsTimelineView.swift b/ElementX/Sources/Screens/MediaEventsTimelineScreen/View/TimelineViews/VideoMediaEventsTimelineView.swift index 77c7fedb9..aba34d9c6 100644 --- a/ElementX/Sources/Screens/MediaEventsTimelineScreen/View/TimelineViews/VideoMediaEventsTimelineView.swift +++ b/ElementX/Sources/Screens/MediaEventsTimelineScreen/View/TimelineViews/VideoMediaEventsTimelineView.swift @@ -28,7 +28,7 @@ struct VideoMediaEventsTimelineView: View { var thumbnail: some View { if let thumbnailSource = timelineItem.content.thumbnailInfo?.source { LoadableImage(mediaSource: thumbnailSource, - mediaType: .timelineItem(uniqueID: timelineItem.id.uniqueID.id), + mediaType: .timelineItem(uniqueID: timelineItem.id.uniqueID), blurhash: timelineItem.content.blurhash, size: timelineItem.content.thumbnailInfo?.size, mediaProvider: context?.mediaProvider) { diff --git a/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/ComposerToolbarModels.swift b/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/ComposerToolbarModels.swift index b88686007..39dd99202 100644 --- a/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/ComposerToolbarModels.swift +++ b/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/ComposerToolbarModels.swift @@ -294,7 +294,7 @@ enum ComposerMode: Equatable { case `default` case reply(eventID: String, replyDetails: TimelineItemReplyDetails, isThread: Bool) - case edit(originalEventOrTransactionID: EventOrTransactionId, type: EditType) + case edit(originalEventOrTransactionID: TimelineItemIdentifier.EventOrTransactionID, type: EditType) case recordVoiceMessage(state: AudioRecorderState) case previewVoiceMessage(state: AudioPlayerState, waveform: WaveformSource, isUploading: Bool) diff --git a/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/ComposerToolbarViewModel.swift b/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/ComposerToolbarViewModel.swift index 7ec6218ed..0f22c1d2f 100644 --- a/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/ComposerToolbarViewModel.swift +++ b/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/ComposerToolbarViewModel.swift @@ -284,7 +284,7 @@ final class ComposerToolbarViewModel: ComposerToolbarViewModelType, ComposerTool case .newMessage: set(mode: .default) case .edit(let eventID): - set(mode: .edit(originalEventOrTransactionID: .eventId(eventId: eventID), type: .default)) + set(mode: .edit(originalEventOrTransactionID: .eventID(eventID), type: .default)) case .reply(let eventID): set(mode: .reply(eventID: eventID, replyDetails: .loading(eventID: eventID), isThread: false)) replyLoadingTask = Task { @@ -340,7 +340,7 @@ final class ComposerToolbarViewModel: ComposerToolbarViewModelType, ComposerTool switch state.composerMode { case .default: type = .newMessage - case .edit(.eventId(let originalEventID), .default): + case .edit(.eventID(let originalEventID), .default): type = .edit(eventID: originalEventID) case .reply(let eventID, _, _): type = .reply(eventID: eventID) diff --git a/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/MessageComposer.swift b/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/MessageComposer.swift index ebb8decb2..0aa263f12 100644 --- a/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/MessageComposer.swift +++ b/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/MessageComposer.swift @@ -302,7 +302,7 @@ struct MessageComposer_Previews: PreviewProvider, TestablePreview { messageComposer() messageComposer(.init(string: "Some message"), - mode: .edit(originalEventOrTransactionID: .eventId(eventId: UUID().uuidString), type: .default)) + mode: .edit(originalEventOrTransactionID: .eventID(UUID().uuidString), type: .default)) messageComposer(mode: .reply(eventID: UUID().uuidString, replyDetails: .loaded(sender: .init(id: "Kirk"), @@ -313,9 +313,9 @@ struct MessageComposer_Previews: PreviewProvider, TestablePreview { Color.clear.frame(height: 20) messageComposer(.init(string: "Some new caption"), - mode: .edit(originalEventOrTransactionID: .eventId(eventId: UUID().uuidString), type: .addCaption)) + mode: .edit(originalEventOrTransactionID: .eventID(UUID().uuidString), type: .addCaption)) messageComposer(.init(string: "Some updated caption"), - mode: .edit(originalEventOrTransactionID: .eventId(eventId: UUID().uuidString), type: .editCaption)) + mode: .edit(originalEventOrTransactionID: .eventID(UUID().uuidString), type: .editCaption)) } .padding(.horizontal) diff --git a/ElementX/Sources/Screens/Timeline/TimelineModels.swift b/ElementX/Sources/Screens/Timeline/TimelineModels.swift index 2840a8449..8eb315f52 100644 --- a/ElementX/Sources/Screens/Timeline/TimelineModels.swift +++ b/ElementX/Sources/Screens/Timeline/TimelineModels.swift @@ -206,9 +206,9 @@ struct TimelineState { // These can be removed when we have full swiftUI and moved as @State values in the view var scrollToBottomPublisher = PassthroughSubject() - var itemsDictionary = OrderedDictionary() + var itemsDictionary = OrderedDictionary() - var uniqueIDs: [TimelineUniqueId] { + var uniqueIDs: [TimelineItemIdentifier.UniqueID] { itemsDictionary.keys.elements } diff --git a/ElementX/Sources/Screens/Timeline/TimelineTableViewController.swift b/ElementX/Sources/Screens/Timeline/TimelineTableViewController.swift index e04f962fb..17910355c 100644 --- a/ElementX/Sources/Screens/Timeline/TimelineTableViewController.swift +++ b/ElementX/Sources/Screens/Timeline/TimelineTableViewController.swift @@ -48,7 +48,7 @@ class TimelineTableViewController: UIViewController { private let coordinator: TimelineView.Coordinator private let tableView = UITableView(frame: .zero, style: .plain) - var timelineItemsDictionary = OrderedDictionary() { + var timelineItemsDictionary = OrderedDictionary() { didSet { guard canApplySnapshot else { hasPendingItems = true @@ -146,12 +146,12 @@ class TimelineTableViewController: UIViewController { @Binding private var isScrolledToBottom: Bool - private var timelineItemsIDs: [TimelineUniqueId] { + private var timelineItemsIDs: [TimelineItemIdentifier.UniqueID] { timelineItemsDictionary.keys.elements.reversed() } /// The table's diffable data source. - private var dataSource: UITableViewDiffableDataSource? + private var dataSource: UITableViewDiffableDataSource? private var cancellables = Set() /// A publisher used to throttle back pagination requests. @@ -247,7 +247,7 @@ class TimelineTableViewController: UIViewController { private func configureDataSource() { dataSource = .init(tableView: tableView) { [weak self] tableView, indexPath, id in switch id { - case TimelineUniqueId(id: TimelineTypingIndicatorCell.reuseIdentifier): + case TimelineItemIdentifier.UniqueID(TimelineTypingIndicatorCell.reuseIdentifier): let cell = tableView.dequeueReusableCell(withIdentifier: TimelineTypingIndicatorCell.reuseIdentifier, for: indexPath) guard let self else { return cell @@ -313,12 +313,12 @@ class TimelineTableViewController: UIViewController { private func applySnapshot() { guard let dataSource else { return } - var snapshot = NSDiffableDataSourceSnapshot() + var snapshot = NSDiffableDataSourceSnapshot() // We don't want to display the typing notification in this timeline if coordinator.context.viewState.timelineKind != .pinned { snapshot.appendSections([.typingIndicator]) - snapshot.appendItems([TimelineUniqueId(id: TimelineTypingIndicatorCell.reuseIdentifier)]) + snapshot.appendItems([TimelineItemIdentifier.UniqueID(TimelineTypingIndicatorCell.reuseIdentifier)]) } snapshot.appendSections([.main]) snapshot.appendItems(timelineItemsIDs) @@ -517,7 +517,7 @@ extension TimelineTableViewController { } /// Returns the frame of the cell for a particular timeline item. - private func cellFrame(for uniqueID: TimelineUniqueId) -> CGRect? { + private func cellFrame(for uniqueID: TimelineItemIdentifier.UniqueID) -> CGRect? { guard let timelineCell = tableView.visibleCells.first(where: { ($0 as? TimelineItemCell)?.item?.identifier.uniqueID == uniqueID }) else { return nil } @@ -537,13 +537,13 @@ extension TimelineTableViewController { } } -private extension NSDiffableDataSourceSnapshot { +private extension NSDiffableDataSourceSnapshot { var numberOfMainItems: Int { guard sectionIdentifiers.contains(.main) else { return 0 } return numberOfItems(inSection: .main) } - var mainItemIdentifiers: [TimelineUniqueId] { + var mainItemIdentifiers: [TimelineItemIdentifier.UniqueID] { guard sectionIdentifiers.contains(.main) else { return [] } return itemIdentifiers(inSection: .main) } diff --git a/ElementX/Sources/Screens/Timeline/TimelineViewModel.swift b/ElementX/Sources/Screens/Timeline/TimelineViewModel.swift index c5ad6a2f4..af2bbc0fb 100644 --- a/ElementX/Sources/Screens/Timeline/TimelineViewModel.swift +++ b/ElementX/Sources/Screens/Timeline/TimelineViewModel.swift @@ -658,7 +658,7 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol { // MARK: - Timeline Item Building private func buildTimelineViews(timelineItems: [RoomTimelineItemProtocol], isSwitchingTimelines: Bool = false) { - var timelineItemsDictionary = OrderedDictionary() + var timelineItemsDictionary = OrderedDictionary() timelineItems.filter { $0 is RedactedRoomTimelineItem }.forEach { timelineItem in // Stops the audio player when a voice message is redacted. diff --git a/ElementX/Sources/Screens/Timeline/View/Style/TimelineItemBubbledStylerView.swift b/ElementX/Sources/Screens/Timeline/View/Style/TimelineItemBubbledStylerView.swift index b6a530ccf..a97e405e2 100644 --- a/ElementX/Sources/Screens/Timeline/View/Style/TimelineItemBubbledStylerView.swift +++ b/ElementX/Sources/Screens/Timeline/View/Style/TimelineItemBubbledStylerView.swift @@ -613,7 +613,7 @@ private struct MockTimelineContent: View { } func makeItemIdentifier() -> TimelineItemIdentifier { - isPinned ? .event(uniqueID: .init(id: ""), eventOrTransactionID: .eventId(eventId: "pinned")) : .randomEvent + isPinned ? .event(uniqueID: .init(""), eventOrTransactionID: .eventID("pinned")) : .randomEvent } var replyDetails: TimelineItemReplyDetails? { diff --git a/ElementX/Sources/Screens/Timeline/View/Style/TimelineStyler.swift b/ElementX/Sources/Screens/Timeline/View/Style/TimelineStyler.swift index 6ae10911f..64379cc79 100644 --- a/ElementX/Sources/Screens/Timeline/View/Style/TimelineStyler.swift +++ b/ElementX/Sources/Screens/Timeline/View/Style/TimelineStyler.swift @@ -79,8 +79,8 @@ struct TimelineItemStyler_Previews: PreviewProvider, TestablePreview { }() static let sendingLast: TextRoomTimelineItem = { - let id = viewModel.state.timelineState.uniqueIDs.last ?? .init(id: UUID().uuidString) - var result = TextRoomTimelineItem(id: .event(uniqueID: id, eventOrTransactionID: .eventId(eventId: UUID().uuidString)), + let id = viewModel.state.timelineState.uniqueIDs.last ?? .init(UUID().uuidString) + var result = TextRoomTimelineItem(id: .event(uniqueID: id, eventOrTransactionID: .eventID(UUID().uuidString)), timestamp: .mock, isOutgoing: true, isEditable: false, @@ -99,8 +99,8 @@ struct TimelineItemStyler_Previews: PreviewProvider, TestablePreview { }() static let sentLast: TextRoomTimelineItem = { - let id = viewModel.state.timelineState.uniqueIDs.last ?? .init(id: UUID().uuidString) - let result = TextRoomTimelineItem(id: .event(uniqueID: id, eventOrTransactionID: .eventId(eventId: UUID().uuidString)), + let id = viewModel.state.timelineState.uniqueIDs.last ?? .init(UUID().uuidString) + let result = TextRoomTimelineItem(id: .event(uniqueID: id, eventOrTransactionID: .eventID(UUID().uuidString)), timestamp: .mock, isOutgoing: true, isEditable: false, diff --git a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/CollapsibleRoomTimelineView.swift b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/CollapsibleRoomTimelineView.swift index 5738e9667..692217556 100644 --- a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/CollapsibleRoomTimelineView.swift +++ b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/CollapsibleRoomTimelineView.swift @@ -52,8 +52,8 @@ struct CollapsibleRoomTimelineView: View { struct CollapsibleRoomTimelineView_Previews: PreviewProvider, TestablePreview { static let item = CollapsibleTimelineItem(items: [ - SeparatorRoomTimelineItem(id: .virtual(uniqueID: .init(id: "First separator")), timestamp: .mock), - SeparatorRoomTimelineItem(id: .virtual(uniqueID: .init(id: "Second separator")), timestamp: .mock) + SeparatorRoomTimelineItem(id: .virtual(uniqueID: .init("First separator")), timestamp: .mock), + SeparatorRoomTimelineItem(id: .virtual(uniqueID: .init("Second separator")), timestamp: .mock) ]) static var previews: some View { diff --git a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/ImageRoomTimelineView.swift b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/ImageRoomTimelineView.swift index af5bf1f7a..af14fd8bc 100644 --- a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/ImageRoomTimelineView.swift +++ b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/ImageRoomTimelineView.swift @@ -44,7 +44,7 @@ struct ImageRoomTimelineView: View { private var loadableImage: some View { if timelineItem.content.contentType == .gif { LoadableImage(mediaSource: timelineItem.content.imageInfo.source, - mediaType: .timelineItem(uniqueID: timelineItem.id.uniqueID.id), + mediaType: .timelineItem(uniqueID: timelineItem.id.uniqueID), blurhash: timelineItem.content.blurhash, size: timelineItem.content.imageInfo.size, mediaProvider: context?.mediaProvider) { @@ -53,7 +53,7 @@ struct ImageRoomTimelineView: View { .timelineMediaFrame(imageInfo: timelineItem.content.imageInfo) } else { LoadableImage(mediaSource: timelineItem.content.thumbnailInfo?.source ?? timelineItem.content.imageInfo.source, - mediaType: .timelineItem(uniqueID: timelineItem.id.uniqueID.id), + mediaType: .timelineItem(uniqueID: timelineItem.id.uniqueID), blurhash: timelineItem.content.blurhash, size: timelineItem.content.thumbnailInfo?.size ?? timelineItem.content.imageInfo.size, mediaProvider: context?.mediaProvider) { diff --git a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/ReadMarkerRoomTimelineView.swift b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/ReadMarkerRoomTimelineView.swift index a60a2132f..3d13d5b93 100644 --- a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/ReadMarkerRoomTimelineView.swift +++ b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/ReadMarkerRoomTimelineView.swift @@ -33,7 +33,7 @@ struct ReadMarkerRoomTimelineView_Previews: PreviewProvider, TestablePreview { static var previews: some View { VStack(alignment: .leading, spacing: 0) { - RoomTimelineItemView(viewState: .init(type: .separator(.init(id: .virtual(uniqueID: .init(id: "Separator")), timestamp: .mock)), groupStyle: .single)) + RoomTimelineItemView(viewState: .init(type: .separator(.init(id: .virtual(uniqueID: .init("Separator")), timestamp: .mock)), groupStyle: .single)) RoomTimelineItemView(viewState: .init(type: .text(.init(id: .randomEvent, timestamp: .mock, isOutgoing: true, @@ -45,7 +45,7 @@ struct ReadMarkerRoomTimelineView_Previews: PreviewProvider, TestablePreview { ReadMarkerRoomTimelineView(timelineItem: item) - RoomTimelineItemView(viewState: .init(type: .separator(.init(id: .virtual(uniqueID: .init(id: "Separator")), timestamp: .mock)), groupStyle: .single)) + RoomTimelineItemView(viewState: .init(type: .separator(.init(id: .virtual(uniqueID: .init("Separator")), timestamp: .mock)), groupStyle: .single)) RoomTimelineItemView(viewState: .init(type: .text(.init(id: .randomEvent, timestamp: .mock, isOutgoing: false, diff --git a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/SeparatorRoomTimelineView.swift b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/SeparatorRoomTimelineView.swift index a00bc2ff1..2535a8c20 100644 --- a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/SeparatorRoomTimelineView.swift +++ b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/SeparatorRoomTimelineView.swift @@ -23,7 +23,7 @@ struct SeparatorRoomTimelineView: View { struct SeparatorRoomTimelineView_Previews: PreviewProvider, TestablePreview { static var previews: some View { - let item = SeparatorRoomTimelineItem(id: .virtual(uniqueID: .init(id: "Separator")), + let item = SeparatorRoomTimelineItem(id: .virtual(uniqueID: .init("Separator")), timestamp: .mock) SeparatorRoomTimelineView(timelineItem: item) } diff --git a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/StickerRoomTimelineView.swift b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/StickerRoomTimelineView.swift index c00106d8f..1c5e4463f 100644 --- a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/StickerRoomTimelineView.swift +++ b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/StickerRoomTimelineView.swift @@ -15,7 +15,7 @@ struct StickerRoomTimelineView: View { var body: some View { TimelineStyler(timelineItem: timelineItem) { LoadableImage(mediaSource: timelineItem.imageInfo.source, - mediaType: .timelineItem(uniqueID: timelineItem.id.uniqueID.id), + mediaType: .timelineItem(uniqueID: timelineItem.id.uniqueID), blurhash: timelineItem.blurhash, size: timelineItem.imageInfo.size, mediaProvider: context?.mediaProvider) { diff --git a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/VideoRoomTimelineView.swift b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/VideoRoomTimelineView.swift index 0bda39aba..6f4e9d8b4 100644 --- a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/VideoRoomTimelineView.swift +++ b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/VideoRoomTimelineView.swift @@ -45,7 +45,7 @@ struct VideoRoomTimelineView: View { var thumbnail: some View { if let thumbnailSource = timelineItem.content.thumbnailInfo?.source { LoadableImage(mediaSource: thumbnailSource, - mediaType: .timelineItem(uniqueID: timelineItem.id.uniqueID.id), + mediaType: .timelineItem(uniqueID: timelineItem.id.uniqueID), blurhash: timelineItem.content.blurhash, size: timelineItem.content.thumbnailInfo?.size, mediaProvider: context?.mediaProvider) { imageView in diff --git a/ElementX/Sources/Services/Room/RoomSummary/RoomSummaryProvider.swift b/ElementX/Sources/Services/Room/RoomSummary/RoomSummaryProvider.swift index a891c0061..de9482351 100644 --- a/ElementX/Sources/Services/Room/RoomSummary/RoomSummaryProvider.swift +++ b/ElementX/Sources/Services/Room/RoomSummary/RoomSummaryProvider.swift @@ -243,7 +243,7 @@ class RoomSummaryProvider: RoomSummaryProviderProtocol { var lastMessageFormattedTimestamp: String? if let latestRoomMessage = roomDetails.latestEvent { - let lastMessage = EventTimelineItemProxy(item: latestRoomMessage, uniqueID: .init(id: "0")) + let lastMessage = EventTimelineItemProxy(item: latestRoomMessage, uniqueID: .init("0")) lastMessageFormattedTimestamp = lastMessage.timestamp.formattedMinimal() attributedLastMessage = eventStringBuilder.buildAttributedString(for: lastMessage) } diff --git a/ElementX/Sources/Services/Timeline/Fixtures/RoomTimelineItemFixtures.swift b/ElementX/Sources/Services/Timeline/Fixtures/RoomTimelineItemFixtures.swift index b13d4c7ba..39dddc90d 100644 --- a/ElementX/Sources/Services/Timeline/Fixtures/RoomTimelineItemFixtures.swift +++ b/ElementX/Sources/Services/Timeline/Fixtures/RoomTimelineItemFixtures.swift @@ -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) } } diff --git a/ElementX/Sources/Services/Timeline/TimelineController/MockTimelineController.swift b/ElementX/Sources/Services/Timeline/TimelineController/MockTimelineController.swift index f33cfa450..d96c2edd4 100644 --- a/ElementX/Sources/Services/Timeline/TimelineController/MockTimelineController.swift +++ b/ElementX/Sources/Services/Timeline/TimelineController/MockTimelineController.swift @@ -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 } diff --git a/ElementX/Sources/Services/Timeline/TimelineController/TimelineController.swift b/ElementX/Sources/Services/Timeline/TimelineController/TimelineController.swift index b39e67960..42bea6fdb 100644 --- a/ElementX/Sources/Services/Timeline/TimelineController/TimelineController.swift +++ b/ElementX/Sources/Services/Timeline/TimelineController/TimelineController.swift @@ -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) { diff --git a/ElementX/Sources/Services/Timeline/TimelineController/TimelineControllerProtocol.swift b/ElementX/Sources/Services/Timeline/TimelineController/TimelineControllerProtocol.swift index 834ece1f1..1753877c3 100644 --- a/ElementX/Sources/Services/Timeline/TimelineController/TimelineControllerProtocol.swift +++ b/ElementX/Sources/Services/Timeline/TimelineController/TimelineControllerProtocol.swift @@ -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 diff --git a/ElementX/Sources/Services/Timeline/TimelineItemIdentifier.swift b/ElementX/Sources/Services/Timeline/TimelineItemIdentifier.swift index 05d2de2e1..bfc181274 100644 --- a/ElementX/Sources/Services/Timeline/TimelineItemIdentifier.swift +++ b/ElementX/Sources/Services/Timeline/TimelineItemIdentifier.swift @@ -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)) } } diff --git a/ElementX/Sources/Services/Timeline/TimelineItemProxy.swift b/ElementX/Sources/Services/Timeline/TimelineItemProxy.swift index 0a82a7e4f..a568aa252 100644 --- a/ElementX/Sources/Services/Timeline/TimelineItemProxy.swift +++ b/ElementX/Sources/Services/Timeline/TimelineItemProxy.swift @@ -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())) } } diff --git a/ElementX/Sources/Services/Timeline/TimelineItems/Items/Virtual/PaginationIndicatorRoomTimelineItem.swift b/ElementX/Sources/Services/Timeline/TimelineItems/Items/Virtual/PaginationIndicatorRoomTimelineItem.swift index 7035b2ae9..2724cb990 100644 --- a/ElementX/Sources/Services/Timeline/TimelineItems/Items/Virtual/PaginationIndicatorRoomTimelineItem.swift +++ b/ElementX/Sources/Services/Timeline/TimelineItems/Items/Virtual/PaginationIndicatorRoomTimelineItem.swift @@ -22,6 +22,6 @@ struct PaginationIndicatorRoomTimelineItem: DecorationTimelineItemProtocol, Equa } init(position: Position) { - id = .virtual(uniqueID: .init(id: position.id)) + id = .virtual(uniqueID: .init(position.id)) } } diff --git a/ElementX/Sources/Services/Timeline/TimelineItems/Items/Virtual/TimelineStartRoomTimelineItem.swift b/ElementX/Sources/Services/Timeline/TimelineItems/Items/Virtual/TimelineStartRoomTimelineItem.swift index b4030948b..785c6daf0 100644 --- a/ElementX/Sources/Services/Timeline/TimelineItems/Items/Virtual/TimelineStartRoomTimelineItem.swift +++ b/ElementX/Sources/Services/Timeline/TimelineItems/Items/Virtual/TimelineStartRoomTimelineItem.swift @@ -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? } diff --git a/ElementX/Sources/Services/Timeline/TimelineItems/RoomTimelineItemViewState.swift b/ElementX/Sources/Services/Timeline/TimelineItems/RoomTimelineItemViewState.swift index 02128db9a..11d31b44f 100644 --- a/ElementX/Sources/Services/Timeline/TimelineItems/RoomTimelineItemViewState.swift +++ b/ElementX/Sources/Services/Timeline/TimelineItems/RoomTimelineItemViewState.swift @@ -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 } } diff --git a/ElementX/Sources/Services/Timeline/TimelineProxy.swift b/ElementX/Sources/Services/Timeline/TimelineProxy.swift index 5672cbc9b..370cc24ea 100644 --- a/ElementX/Sources/Services/Timeline/TimelineProxy.swift +++ b/ElementX/Sources/Services/Timeline/TimelineProxy.swift @@ -166,9 +166,9 @@ final class TimelineProxy: TimelineProxyProtocol { } } - func edit(_ eventOrTransactionID: EventOrTransactionId, newContent: EditedContent) async -> Result { + func edit(_ eventOrTransactionID: TimelineItemIdentifier.EventOrTransactionID, newContent: EditedContent) async -> Result { 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 { + func redact(_ eventOrTransactionID: TimelineItemIdentifier.EventOrTransactionID, reason: String?) async -> Result { 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 { + func toggleReaction(_ reaction: String, to eventOrTransactionID: TimelineItemIdentifier.EventOrTransactionID) async -> Result { 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, diff --git a/ElementX/Sources/Services/Timeline/TimelineProxyProtocol.swift b/ElementX/Sources/Services/Timeline/TimelineProxyProtocol.swift index 0f90c48f3..a175b43fa 100644 --- a/ElementX/Sources/Services/Timeline/TimelineProxyProtocol.swift +++ b/ElementX/Sources/Services/Timeline/TimelineProxyProtocol.swift @@ -40,10 +40,10 @@ protocol TimelineProxyProtocol { func paginateBackwards(requestSize: UInt16) async -> Result func paginateForwards(requestSize: UInt16) async -> Result - func edit(_ eventOrTransactionID: EventOrTransactionId, + func edit(_ eventOrTransactionID: TimelineItemIdentifier.EventOrTransactionID, newContent: EditedContent) async -> Result - func redact(_ eventOrTransactionID: EventOrTransactionId, + func redact(_ eventOrTransactionID: TimelineItemIdentifier.EventOrTransactionID, reason: String?) async -> Result func pin(eventID: String) async -> Result @@ -94,7 +94,7 @@ protocol TimelineProxyProtocol { inReplyToEventID: String?, intentionalMentions: IntentionalMentions) async -> Result - func toggleReaction(_ reaction: String, to eventID: EventOrTransactionId) async -> Result + func toggleReaction(_ reaction: String, to eventID: TimelineItemIdentifier.EventOrTransactionID) async -> Result func createPoll(question: String, answers: [String], pollKind: Poll.Kind) async -> Result diff --git a/UnitTests/Sources/ComposerToolbarViewModelTests.swift b/UnitTests/Sources/ComposerToolbarViewModelTests.swift index f458e0b66..0a6482a47 100644 --- a/UnitTests/Sources/ComposerToolbarViewModelTests.swift +++ b/UnitTests/Sources/ComposerToolbarViewModelTests.swift @@ -32,14 +32,14 @@ class ComposerToolbarViewModelTests: XCTestCase { } func testComposerFocus() { - viewModel.process(timelineAction: .setMode(mode: .edit(originalEventOrTransactionID: .eventId(eventId: "mock"), type: .default))) + viewModel.process(timelineAction: .setMode(mode: .edit(originalEventOrTransactionID: .eventID("mock"), type: .default))) XCTAssertTrue(viewModel.state.bindings.composerFocused) viewModel.process(timelineAction: .removeFocus) XCTAssertFalse(viewModel.state.bindings.composerFocused) } func testComposerMode() { - let mode: ComposerMode = .edit(originalEventOrTransactionID: .eventId(eventId: "mock"), type: .default) + let mode: ComposerMode = .edit(originalEventOrTransactionID: .eventID("mock"), type: .default) viewModel.process(timelineAction: .setMode(mode: mode)) XCTAssertEqual(viewModel.state.composerMode, mode) viewModel.process(timelineAction: .clear) @@ -47,7 +47,7 @@ class ComposerToolbarViewModelTests: XCTestCase { } func testComposerModeIsPublished() { - let mode: ComposerMode = .edit(originalEventOrTransactionID: .eventId(eventId: "mock"), type: .default) + let mode: ComposerMode = .edit(originalEventOrTransactionID: .eventID("mock"), type: .default) let expectation = expectation(description: "Composer mode is published") let cancellable = viewModel .context @@ -229,7 +229,7 @@ class ComposerToolbarViewModelTests: XCTestCase { } viewModel.context.composerFormattingEnabled = false - viewModel.process(timelineAction: .setMode(mode: .edit(originalEventOrTransactionID: .eventId(eventId: "testID"), type: .default))) + viewModel.process(timelineAction: .setMode(mode: .edit(originalEventOrTransactionID: .eventID("testID"), type: .default))) viewModel.context.plainComposerText = .init(string: "Hello world!") viewModel.saveDraft() @@ -388,7 +388,7 @@ class ComposerToolbarViewModelTests: XCTestCase { await fulfillment(of: [expectation], timeout: 10) XCTAssertFalse(viewModel.context.composerFormattingEnabled) - XCTAssertEqual(viewModel.state.composerMode, .edit(originalEventOrTransactionID: .eventId(eventId: "testID"), type: .default)) + XCTAssertEqual(viewModel.state.composerMode, .edit(originalEventOrTransactionID: .eventID("testID"), type: .default)) XCTAssertEqual(viewModel.context.plainComposerText, NSAttributedString(string: "Hello world!")) } @@ -476,7 +476,7 @@ class ComposerToolbarViewModelTests: XCTestCase { func testSaveVolatileDraftWhenEditing() { viewModel.context.composerFormattingEnabled = false viewModel.context.plainComposerText = .init(string: "Hello world!") - viewModel.process(timelineAction: .setMode(mode: .edit(originalEventOrTransactionID: .eventId(eventId: UUID().uuidString), type: .default))) + viewModel.process(timelineAction: .setMode(mode: .edit(originalEventOrTransactionID: .eventID(UUID().uuidString), type: .default))) let draft = draftServiceMock.saveVolatileDraftReceivedDraft XCTAssertNotNil(draft) diff --git a/UnitTests/Sources/LoggingTests.swift b/UnitTests/Sources/LoggingTests.swift index 830f93154..ffff8db60 100644 --- a/UnitTests/Sources/LoggingTests.swift +++ b/UnitTests/Sources/LoggingTests.swift @@ -194,25 +194,25 @@ class LoggingTests: XCTestCase { } let content = try String(contentsOf: logFile) - XCTAssertTrue(content.contains(textMessage.id.uniqueID.id)) + XCTAssertTrue(content.contains(textMessage.id.uniqueID.value)) XCTAssertFalse(content.contains(textMessage.body)) XCTAssertFalse(content.contains(textAttributedString)) - XCTAssertTrue(content.contains(noticeMessage.id.uniqueID.id)) + XCTAssertTrue(content.contains(noticeMessage.id.uniqueID.value)) XCTAssertFalse(content.contains(noticeMessage.body)) XCTAssertFalse(content.contains(noticeAttributedString)) - XCTAssertTrue(content.contains(emoteMessage.id.uniqueID.id)) + XCTAssertTrue(content.contains(emoteMessage.id.uniqueID.value)) XCTAssertFalse(content.contains(emoteMessage.body)) XCTAssertFalse(content.contains(emoteAttributedString)) - XCTAssertTrue(content.contains(imageMessage.id.uniqueID.id)) + XCTAssertTrue(content.contains(imageMessage.id.uniqueID.value)) XCTAssertFalse(content.contains(imageMessage.body)) - XCTAssertTrue(content.contains(videoMessage.id.uniqueID.id)) + XCTAssertTrue(content.contains(videoMessage.id.uniqueID.value)) XCTAssertFalse(content.contains(videoMessage.body)) - XCTAssertTrue(content.contains(fileMessage.id.uniqueID.id)) + XCTAssertTrue(content.contains(fileMessage.id.uniqueID.value)) XCTAssertFalse(content.contains(fileMessage.body)) } diff --git a/UnitTests/Sources/MessageForwardingScreenViewModelTests.swift b/UnitTests/Sources/MessageForwardingScreenViewModelTests.swift index b739d230b..946bfceb9 100644 --- a/UnitTests/Sources/MessageForwardingScreenViewModelTests.swift +++ b/UnitTests/Sources/MessageForwardingScreenViewModelTests.swift @@ -12,7 +12,7 @@ import XCTest @MainActor class MessageForwardingScreenViewModelTests: XCTestCase { - let forwardingItem = MessageForwardingItem(id: .event(uniqueID: .init(id: "t1"), eventOrTransactionID: .eventId(eventId: "t1")), + let forwardingItem = MessageForwardingItem(id: .event(uniqueID: .init("t1"), eventOrTransactionID: .eventID("t1")), roomID: "1", content: .init(noPointer: .init())) var viewModel: MessageForwardingScreenViewModelProtocol! diff --git a/UnitTests/Sources/RoomScreenViewModelTests.swift b/UnitTests/Sources/RoomScreenViewModelTests.swift index 4120c4690..537d46637 100644 --- a/UnitTests/Sources/RoomScreenViewModelTests.swift +++ b/UnitTests/Sources/RoomScreenViewModelTests.swift @@ -70,8 +70,8 @@ class RoomScreenViewModelTests: XCTestCase { let providerUpdateSubject = PassthroughSubject<([TimelineItemProxy], PaginationState), Never>() pinnedTimelineProviderMock.underlyingUpdatePublisher = providerUpdateSubject.eraseToAnyPublisher() pinnedTimelineMock.timelineProvider = pinnedTimelineProviderMock - pinnedTimelineProviderMock.itemProxies = [.event(.init(item: EventTimelineItem(configuration: .init(eventID: "test1")), uniqueID: .init(id: "1"))), - .event(.init(item: EventTimelineItem(configuration: .init(eventID: "test2")), uniqueID: .init(id: "2")))] + pinnedTimelineProviderMock.itemProxies = [.event(.init(item: EventTimelineItem(configuration: .init(eventID: "test1")), uniqueID: .init("1"))), + .event(.init(item: EventTimelineItem(configuration: .init(eventID: "test2")), uniqueID: .init("2")))] // check if the banner is now in a loaded state and is showing the counter deferred = deferFulfillment(viewModel.context.$viewState) { viewState in @@ -87,9 +87,9 @@ class RoomScreenViewModelTests: XCTestCase { deferred = deferFulfillment(viewModel.context.$viewState) { viewState in viewState.pinnedEventsBannerState.count == 3 } - providerUpdateSubject.send(([.event(.init(item: EventTimelineItem(configuration: .init(eventID: "test1")), uniqueID: .init(id: "1"))), - .event(.init(item: EventTimelineItem(configuration: .init(eventID: "test2")), uniqueID: .init(id: "2"))), - .event(.init(item: EventTimelineItem(configuration: .init(eventID: "test3")), uniqueID: .init(id: "3")))], .initial)) + providerUpdateSubject.send(([.event(.init(item: EventTimelineItem(configuration: .init(eventID: "test1")), uniqueID: .init("1"))), + .event(.init(item: EventTimelineItem(configuration: .init(eventID: "test2")), uniqueID: .init("2"))), + .event(.init(item: EventTimelineItem(configuration: .init(eventID: "test3")), uniqueID: .init("3")))], .initial)) try await deferred.fulfill() XCTAssertFalse(viewModel.context.viewState.pinnedEventsBannerState.isLoading) XCTAssertTrue(viewModel.context.viewState.shouldShowPinnedEventsBanner) @@ -110,9 +110,9 @@ class RoomScreenViewModelTests: XCTestCase { let pinnedTimelineProviderMock = TimelineProviderMock() pinnedTimelineMock.timelineProvider = pinnedTimelineProviderMock pinnedTimelineProviderMock.underlyingUpdatePublisher = Empty<([TimelineItemProxy], PaginationState), Never>().eraseToAnyPublisher() - pinnedTimelineProviderMock.itemProxies = [.event(.init(item: EventTimelineItem(configuration: .init(eventID: "test1")), uniqueID: .init(id: "1"))), - .event(.init(item: EventTimelineItem(configuration: .init(eventID: "test2")), uniqueID: .init(id: "2"))), - .event(.init(item: EventTimelineItem(configuration: .init(eventID: "test3")), uniqueID: .init(id: "3")))] + pinnedTimelineProviderMock.itemProxies = [.event(.init(item: EventTimelineItem(configuration: .init(eventID: "test1")), uniqueID: .init("1"))), + .event(.init(item: EventTimelineItem(configuration: .init(eventID: "test2")), uniqueID: .init("2"))), + .event(.init(item: EventTimelineItem(configuration: .init(eventID: "test3")), uniqueID: .init("3")))] roomProxyMock.underlyingPinnedEventsTimeline = pinnedTimelineMock let viewModel = RoomScreenViewModel(clientProxy: ClientProxyMock(), roomProxy: roomProxyMock, diff --git a/UnitTests/Sources/TimelineItemFactoryTests.swift b/UnitTests/Sources/TimelineItemFactoryTests.swift index 947b3776e..210b0da55 100644 --- a/UnitTests/Sources/TimelineItemFactoryTests.swift +++ b/UnitTests/Sources/TimelineItemFactoryTests.swift @@ -22,7 +22,7 @@ class TimelineItemFactoryTests: XCTestCase { let eventTimelineItem = EventTimelineItem.mockCallInvite(sender: senderUserID) - let eventTimelineItemProxy = EventTimelineItemProxy(item: eventTimelineItem, uniqueID: .init(id: "0")) + let eventTimelineItemProxy = EventTimelineItemProxy(item: eventTimelineItem, uniqueID: .init("0")) let item = factory.buildTimelineItem(for: eventTimelineItemProxy, isDM: false) diff --git a/UnitTests/Sources/TimelineViewModelTests.swift b/UnitTests/Sources/TimelineViewModelTests.swift index fbc227aab..31bedc14f 100644 --- a/UnitTests/Sources/TimelineViewModelTests.swift +++ b/UnitTests/Sources/TimelineViewModelTests.swift @@ -259,9 +259,9 @@ class TimelineViewModelTests: XCTestCase { func testSendReadReceiptWithoutEvents() async throws { // Given a room with only virtual items. - let items = [SeparatorRoomTimelineItem(uniqueID: .init(id: "v1")), - SeparatorRoomTimelineItem(uniqueID: .init(id: "v2")), - SeparatorRoomTimelineItem(uniqueID: .init(id: "v3"))] + let items = [SeparatorRoomTimelineItem(uniqueID: .init("v1")), + SeparatorRoomTimelineItem(uniqueID: .init("v2")), + SeparatorRoomTimelineItem(uniqueID: .init("v3"))] let (viewModel, _, timelineProxy, _) = readReceiptsConfiguration(with: items) // When sending a read receipt for the last item. @@ -276,7 +276,7 @@ class TimelineViewModelTests: XCTestCase { // Given a room where the last event is a virtual item. let items: [RoomTimelineItemProtocol] = [TextRoomTimelineItem(eventID: "t1"), TextRoomTimelineItem(eventID: "t2"), - SeparatorRoomTimelineItem(uniqueID: .init(id: "v3"))] + SeparatorRoomTimelineItem(uniqueID: .init("v3"))] let (viewModel, _, _, _) = readReceiptsConfiguration(with: items) // When sending a read receipt for the last item. @@ -445,14 +445,14 @@ private extension TextRoomTimelineItem { } private extension SeparatorRoomTimelineItem { - init(uniqueID: TimelineUniqueId) { + init(uniqueID: TimelineItemIdentifier.UniqueID) { self.init(id: .virtual(uniqueID: uniqueID), timestamp: .mock) } } private extension TextRoomTimelineItem { init(eventID: String) { - self.init(id: .event(uniqueID: .init(id: UUID().uuidString), eventOrTransactionID: .eventId(eventId: eventID)), + self.init(id: .event(uniqueID: .init(UUID().uuidString), eventOrTransactionID: .eventID(eventID)), timestamp: .mock, isOutgoing: false, isEditable: false,