From d357fd945101103e8cc52cb7b77b46c56e002f61 Mon Sep 17 00:00:00 2001 From: Stefan Ceriu Date: Wed, 16 Oct 2024 14:07:54 +0300 Subject: [PATCH] Refactor the`TimelineItemIdentifier` handling; stop relying on optional `EventOrTransactionId`s and be explicit when setting composer modes from the draft service. --- ElementX.xcodeproj/project.pbxproj | 4 ++ .../Sources/Application/AppCoordinator.swift | 1 + .../Mocks/Generated/GeneratedMocks.swift | 62 ++++++++--------- ElementX/Sources/Mocks/PollMock.swift | 2 +- .../View/MessageForwardingScreen.swift | 2 +- ...ResolveVerifiedUserSendFailureScreen.swift | 4 +- .../ComposerToolbarModels.swift | 14 ++-- .../ComposerToolbarViewModel.swift | 20 ++---- .../View/ComposerToolbar.swift | 4 +- .../View/MessageComposer.swift | 8 +-- .../Timeline/TimelineInteractionHandler.swift | 9 ++- .../Screens/Timeline/TimelineViewModel.swift | 10 +-- .../View/Style/SwipeToReplyView.swift | 2 +- .../Style/TimelineItemBubbledStylerView.swift | 22 +++---- .../Style/TimelineItemSendInfoLabel.swift | 10 +-- .../Timeline/View/Style/TimelineStyler.swift | 18 ++--- .../Supplementary/TimelineReactionsView.swift | 8 +-- .../TimelineReadReceiptsView.swift | 2 +- .../AudioRoomTimelineView.swift | 2 +- .../CallInviteRoomTimelineView.swift | 2 +- .../CallNotificationRoomTimelineView.swift | 2 +- .../CollapsibleRoomTimelineView.swift | 4 +- .../EmoteRoomTimelineView.swift | 2 +- .../EncryptedRoomTimelineView.swift | 4 +- .../FileRoomTimelineView.swift | 6 +- .../ImageRoomTimelineView.swift | 6 +- .../LocationRoomTimelineView.swift | 6 +- .../NoticeRoomTimelineView.swift | 2 +- .../ReadMarkerRoomTimelineView.swift | 10 +-- .../RedactedRoomTimelineView.swift | 2 +- .../SeparatorRoomTimelineView.swift | 3 +- .../StateRoomTimelineView.swift | 2 +- .../StickerRoomTimelineView.swift | 6 +- .../TextRoomTimelineView.swift | 4 +- .../UnsupportedRoomTimelineView.swift | 2 +- .../VideoRoomTimelineView.swift | 6 +- .../Fixtures/RoomTimelineItemFixtures.swift | 40 +++++------ .../MockRoomTimelineController.swift | 5 +- .../RoomTimelineController.swift | 52 +++++---------- .../RoomTimelineControllerProtocol.swift | 16 +---- .../Timeline/TimelineItemIdentifier.swift | 66 +++++++++++++++++++ .../Services/Timeline/TimelineItemProxy.swift | 36 +--------- .../VoiceMessageRoomPlaybackView.swift | 2 +- .../VoiceMessageRoomTimelineView.swift | 2 +- .../PaginationIndicatorRoomTimelineItem.swift | 2 +- .../TimelineStartRoomTimelineItem.swift | 2 +- .../Services/Timeline/TimelineProxy.swift | 36 ++++++---- .../Timeline/TimelineProxyProtocol.swift | 17 +---- UnitTests/Sources/AudioPlayerStateTests.swift | 4 +- .../ComposerToolbarViewModelTests.swift | 22 +++---- UnitTests/Sources/LoggingTests.swift | 12 ++-- .../Sources/MediaPlayerProviderTests.swift | 6 +- ...essageForwardingScreenViewModelTests.swift | 2 +- ...dUserSendFailureScreenViewModelTests.swift | 2 +- .../Sources/TextBasedRoomTimelineTests.swift | 8 +-- .../Sources/TimelineViewModelTests.swift | 6 +- 56 files changed, 313 insertions(+), 298 deletions(-) create mode 100644 ElementX/Sources/Services/Timeline/TimelineItemIdentifier.swift diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index 1af189e9c8..b1b8124cdd 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -611,6 +611,7 @@ 86F9D3028A1F4AE819D75560 /* RoomChangePermissionsScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D879FC4E881E748BB9B34DC /* RoomChangePermissionsScreenCoordinator.swift */; }; 872A6457DF573AF8CEAE927A /* LoginHomeserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9349F590E35CE514A71E6764 /* LoginHomeserver.swift */; }; 874FEFB9D4A4AF447E0E086E /* AuthenticationStartScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = E0F7CCC4A9D1927223F559D5 /* AuthenticationStartScreenViewModelProtocol.swift */; }; + 877D3CE8680536DB430DE6A2 /* TimelineItemIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = E48C91C8BE55CAE1A3DBC3BC /* TimelineItemIdentifier.swift */; }; 878070573C7BF19E735707B4 /* RoomTimelineItemProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DE8D25D6A91030175D52A20 /* RoomTimelineItemProperties.swift */; }; 87B4E59A4467F8EC75F82372 /* VoiceMessageRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B70A50C41C5871B4DB905E7E /* VoiceMessageRoomTimelineView.swift */; }; 87CEA3E07B602705BC2D2A20 /* ClientBuilderHook.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AC0CD1CAFD3F8B057F9AEA5 /* ClientBuilderHook.swift */; }; @@ -2189,6 +2190,7 @@ E44E35AA87F49503E7B3BF6E /* AudioConverter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioConverter.swift; sourceTree = ""; }; E45EBAFF1A83538D54ABDF92 /* ServerSelectionScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerSelectionScreenViewModelTests.swift; sourceTree = ""; }; E461B3C8BBBFCA400B268D14 /* AppRouteURLParserTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppRouteURLParserTests.swift; sourceTree = ""; }; + E48C91C8BE55CAE1A3DBC3BC /* TimelineItemIdentifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineItemIdentifier.swift; sourceTree = ""; }; E5272BC4A60B6AD7553BACA1 /* BlurHashDecode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlurHashDecode.swift; sourceTree = ""; }; E53BFB7E4F329621C844E8C3 /* AnalyticsPromptScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsPromptScreen.swift; sourceTree = ""; }; E55B5EA766E89FF1F87C3ACB /* RoomNotificationSettingsProxyProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomNotificationSettingsProxyProtocol.swift; sourceTree = ""; }; @@ -5473,6 +5475,7 @@ 0DF5CBAF69BDF5DF31C661E1 /* IntentionalMentions.swift */, 66F2402D738694F98729A441 /* RoomTimelineProvider.swift */, 095AED4CF56DFF3EB7BB84C8 /* RoomTimelineProviderProtocol.swift */, + E48C91C8BE55CAE1A3DBC3BC /* TimelineItemIdentifier.swift */, 2D505843AB66822EB91F0DF0 /* TimelineItemProxy.swift */, 55AEEF8142DF1B59DB40FB93 /* TimelineItemSender.swift */, F9E543072DE58E751F028998 /* TimelineProxy.swift */, @@ -6931,6 +6934,7 @@ E6FA87F773424B27614B23E9 /* TimelineItemAccessibilityModifier.swift in Sources */, 79959F8E45C3749997482A7F /* TimelineItemBubbledStylerView.swift in Sources */, A808DC3F72D15C6C5A52317E /* TimelineItemDebugView.swift in Sources */, + 877D3CE8680536DB430DE6A2 /* TimelineItemIdentifier.swift in Sources */, C0B97FFEC0083F3A36609E61 /* TimelineItemMacContextMenu.swift in Sources */, 6C98153D60FF9B648C166C27 /* TimelineItemMenu.swift in Sources */, AE07F215EBC2B9CBF17AA54B /* TimelineItemMenuAction.swift in Sources */, diff --git a/ElementX/Sources/Application/AppCoordinator.swift b/ElementX/Sources/Application/AppCoordinator.swift index 5652a91f2f..75fa3da1d3 100644 --- a/ElementX/Sources/Application/AppCoordinator.swift +++ b/ElementX/Sources/Application/AppCoordinator.swift @@ -334,6 +334,7 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg switch await roomProxy.timeline.sendMessage(replyText, html: nil, + inReplyToEventID: nil, intentionalMentions: .empty) { case .success: break diff --git a/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift b/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift index db66e04c9c..e3c0bace6a 100644 --- a/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift +++ b/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift @@ -13987,8 +13987,8 @@ class TimelineProxyMock: TimelineProxyProtocol { var editNewContentCalled: Bool { return editNewContentCallsCount > 0 } - var editNewContentReceivedArguments: (timelineItem: EventTimelineItem, newContent: RoomMessageEventContentWithoutRelation)? - var editNewContentReceivedInvocations: [(timelineItem: EventTimelineItem, newContent: RoomMessageEventContentWithoutRelation)] = [] + var editNewContentReceivedArguments: (eventOrTransactionID: EventOrTransactionId, newContent: RoomMessageEventContentWithoutRelation)? + var editNewContentReceivedInvocations: [(eventOrTransactionID: EventOrTransactionId, newContent: RoomMessageEventContentWithoutRelation)] = [] var editNewContentUnderlyingReturnValue: Result! var editNewContentReturnValue: Result! { @@ -14014,16 +14014,16 @@ class TimelineProxyMock: TimelineProxyProtocol { } } } - var editNewContentClosure: ((EventTimelineItem, RoomMessageEventContentWithoutRelation) async -> Result)? + var editNewContentClosure: ((EventOrTransactionId, RoomMessageEventContentWithoutRelation) async -> Result)? - func edit(_ timelineItem: EventTimelineItem, newContent: RoomMessageEventContentWithoutRelation) async -> Result { + func edit(_ eventOrTransactionID: EventOrTransactionId, newContent: RoomMessageEventContentWithoutRelation) async -> Result { editNewContentCallsCount += 1 - editNewContentReceivedArguments = (timelineItem: timelineItem, newContent: newContent) + editNewContentReceivedArguments = (eventOrTransactionID: eventOrTransactionID, newContent: newContent) DispatchQueue.main.async { - self.editNewContentReceivedInvocations.append((timelineItem: timelineItem, newContent: newContent)) + self.editNewContentReceivedInvocations.append((eventOrTransactionID: eventOrTransactionID, newContent: newContent)) } if let editNewContentClosure = editNewContentClosure { - return await editNewContentClosure(timelineItem, newContent) + return await editNewContentClosure(eventOrTransactionID, newContent) } else { return editNewContentReturnValue } @@ -14770,15 +14770,15 @@ class TimelineProxyMock: TimelineProxyProtocol { } //MARK: - sendMessage - var sendMessageHtmlInReplyToIntentionalMentionsUnderlyingCallsCount = 0 - var sendMessageHtmlInReplyToIntentionalMentionsCallsCount: Int { + var sendMessageHtmlInReplyToEventIDIntentionalMentionsUnderlyingCallsCount = 0 + var sendMessageHtmlInReplyToEventIDIntentionalMentionsCallsCount: Int { get { if Thread.isMainThread { - return sendMessageHtmlInReplyToIntentionalMentionsUnderlyingCallsCount + return sendMessageHtmlInReplyToEventIDIntentionalMentionsUnderlyingCallsCount } else { var returnValue: Int? = nil DispatchQueue.main.sync { - returnValue = sendMessageHtmlInReplyToIntentionalMentionsUnderlyingCallsCount + returnValue = sendMessageHtmlInReplyToEventIDIntentionalMentionsUnderlyingCallsCount } return returnValue! @@ -14786,29 +14786,29 @@ class TimelineProxyMock: TimelineProxyProtocol { } set { if Thread.isMainThread { - sendMessageHtmlInReplyToIntentionalMentionsUnderlyingCallsCount = newValue + sendMessageHtmlInReplyToEventIDIntentionalMentionsUnderlyingCallsCount = newValue } else { DispatchQueue.main.sync { - sendMessageHtmlInReplyToIntentionalMentionsUnderlyingCallsCount = newValue + sendMessageHtmlInReplyToEventIDIntentionalMentionsUnderlyingCallsCount = newValue } } } } - var sendMessageHtmlInReplyToIntentionalMentionsCalled: Bool { - return sendMessageHtmlInReplyToIntentionalMentionsCallsCount > 0 + var sendMessageHtmlInReplyToEventIDIntentionalMentionsCalled: Bool { + return sendMessageHtmlInReplyToEventIDIntentionalMentionsCallsCount > 0 } - var sendMessageHtmlInReplyToIntentionalMentionsReceivedArguments: (message: String, html: String?, eventID: String?, intentionalMentions: IntentionalMentions)? - var sendMessageHtmlInReplyToIntentionalMentionsReceivedInvocations: [(message: String, html: String?, eventID: String?, intentionalMentions: IntentionalMentions)] = [] + var sendMessageHtmlInReplyToEventIDIntentionalMentionsReceivedArguments: (message: String, html: String?, inReplyToEventID: String?, intentionalMentions: IntentionalMentions)? + var sendMessageHtmlInReplyToEventIDIntentionalMentionsReceivedInvocations: [(message: String, html: String?, inReplyToEventID: String?, intentionalMentions: IntentionalMentions)] = [] - var sendMessageHtmlInReplyToIntentionalMentionsUnderlyingReturnValue: Result! - var sendMessageHtmlInReplyToIntentionalMentionsReturnValue: Result! { + var sendMessageHtmlInReplyToEventIDIntentionalMentionsUnderlyingReturnValue: Result! + var sendMessageHtmlInReplyToEventIDIntentionalMentionsReturnValue: Result! { get { if Thread.isMainThread { - return sendMessageHtmlInReplyToIntentionalMentionsUnderlyingReturnValue + return sendMessageHtmlInReplyToEventIDIntentionalMentionsUnderlyingReturnValue } else { var returnValue: Result? = nil DispatchQueue.main.sync { - returnValue = sendMessageHtmlInReplyToIntentionalMentionsUnderlyingReturnValue + returnValue = sendMessageHtmlInReplyToEventIDIntentionalMentionsUnderlyingReturnValue } return returnValue! @@ -14816,26 +14816,26 @@ class TimelineProxyMock: TimelineProxyProtocol { } set { if Thread.isMainThread { - sendMessageHtmlInReplyToIntentionalMentionsUnderlyingReturnValue = newValue + sendMessageHtmlInReplyToEventIDIntentionalMentionsUnderlyingReturnValue = newValue } else { DispatchQueue.main.sync { - sendMessageHtmlInReplyToIntentionalMentionsUnderlyingReturnValue = newValue + sendMessageHtmlInReplyToEventIDIntentionalMentionsUnderlyingReturnValue = newValue } } } } - var sendMessageHtmlInReplyToIntentionalMentionsClosure: ((String, String?, String?, IntentionalMentions) async -> Result)? + var sendMessageHtmlInReplyToEventIDIntentionalMentionsClosure: ((String, String?, String?, IntentionalMentions) async -> Result)? - func sendMessage(_ message: String, html: String?, inReplyTo eventID: String?, intentionalMentions: IntentionalMentions) async -> Result { - sendMessageHtmlInReplyToIntentionalMentionsCallsCount += 1 - sendMessageHtmlInReplyToIntentionalMentionsReceivedArguments = (message: message, html: html, eventID: eventID, intentionalMentions: intentionalMentions) + func sendMessage(_ message: String, html: String?, inReplyToEventID: String?, intentionalMentions: IntentionalMentions) async -> Result { + sendMessageHtmlInReplyToEventIDIntentionalMentionsCallsCount += 1 + sendMessageHtmlInReplyToEventIDIntentionalMentionsReceivedArguments = (message: message, html: html, inReplyToEventID: inReplyToEventID, intentionalMentions: intentionalMentions) DispatchQueue.main.async { - self.sendMessageHtmlInReplyToIntentionalMentionsReceivedInvocations.append((message: message, html: html, eventID: eventID, intentionalMentions: intentionalMentions)) + self.sendMessageHtmlInReplyToEventIDIntentionalMentionsReceivedInvocations.append((message: message, html: html, inReplyToEventID: inReplyToEventID, intentionalMentions: intentionalMentions)) } - if let sendMessageHtmlInReplyToIntentionalMentionsClosure = sendMessageHtmlInReplyToIntentionalMentionsClosure { - return await sendMessageHtmlInReplyToIntentionalMentionsClosure(message, html, eventID, intentionalMentions) + if let sendMessageHtmlInReplyToEventIDIntentionalMentionsClosure = sendMessageHtmlInReplyToEventIDIntentionalMentionsClosure { + return await sendMessageHtmlInReplyToEventIDIntentionalMentionsClosure(message, html, inReplyToEventID, intentionalMentions) } else { - return sendMessageHtmlInReplyToIntentionalMentionsReturnValue + return sendMessageHtmlInReplyToEventIDIntentionalMentionsReturnValue } } //MARK: - toggleReaction diff --git a/ElementX/Sources/Mocks/PollMock.swift b/ElementX/Sources/Mocks/PollMock.swift index 0da15ef331..821550f415 100644 --- a/ElementX/Sources/Mocks/PollMock.swift +++ b/ElementX/Sources/Mocks/PollMock.swift @@ -82,7 +82,7 @@ extension Poll.Option { extension PollRoomTimelineItem { static func mock(poll: Poll, isOutgoing: Bool = true, isEditable: Bool = false) -> Self { - .init(id: .random, + .init(id: .randomEvent, poll: poll, body: "poll", timestamp: "Now", diff --git a/ElementX/Sources/Screens/MessageForwardingScreen/View/MessageForwardingScreen.swift b/ElementX/Sources/Screens/MessageForwardingScreen/View/MessageForwardingScreen.swift index 8dc876e85b..2801f66ef2 100644 --- a/ElementX/Sources/Screens/MessageForwardingScreen/View/MessageForwardingScreen.swift +++ b/ElementX/Sources/Screens/MessageForwardingScreen/View/MessageForwardingScreen.swift @@ -93,7 +93,7 @@ private struct MessageForwardingListRow: View { struct MessageForwardingScreen_Previews: PreviewProvider, TestablePreview { static var previews: some View { let summaryProvider = RoomSummaryProviderMock(.init(state: .loaded(.mockRooms))) - let viewModel = MessageForwardingScreenViewModel(forwardingItem: .init(id: .init(uniqueID: ""), + let viewModel = MessageForwardingScreenViewModel(forwardingItem: .init(id: .randomEvent, roomID: "", content: .init(noPointer: .init())), clientProxy: ClientProxyMock(.init()), diff --git a/ElementX/Sources/Screens/ResolveVerifiedUserSendFailureScreen/View/ResolveVerifiedUserSendFailureScreen.swift b/ElementX/Sources/Screens/ResolveVerifiedUserSendFailureScreen/View/ResolveVerifiedUserSendFailureScreen.swift index 2d2df80a60..2964e317ba 100644 --- a/ElementX/Sources/Screens/ResolveVerifiedUserSendFailureScreen/View/ResolveVerifiedUserSendFailureScreen.swift +++ b/ElementX/Sources/Screens/ResolveVerifiedUserSendFailureScreen/View/ResolveVerifiedUserSendFailureScreen.swift @@ -94,7 +94,7 @@ struct ResolveVerifiedUserSendFailureScreen_Previews: PreviewProvider, TestableP static func makeViewModel(failure: TimelineItemSendFailure.VerifiedUser) -> ResolveVerifiedUserSendFailureScreenViewModel { ResolveVerifiedUserSendFailureScreenViewModel(failure: failure, - itemID: .random, + itemID: .randomEvent, roomProxy: JoinedRoomProxyMock(.init()), userIndicatorController: UserIndicatorControllerMock()) } @@ -102,7 +102,7 @@ struct ResolveVerifiedUserSendFailureScreen_Previews: PreviewProvider, TestableP struct ResolveVerifiedUserSendFailureScreenSheet_Previews: PreviewProvider { static let viewModel = ResolveVerifiedUserSendFailureScreenViewModel(failure: .changedIdentity(users: ["@alice:matrix.org"]), - itemID: .random, + itemID: .randomEvent, roomProxy: JoinedRoomProxyMock(.init()), userIndicatorController: UserIndicatorControllerMock()) diff --git a/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/ComposerToolbarModels.swift b/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/ComposerToolbarModels.swift index 9f3f169bfb..8f5fc54e37 100644 --- a/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/ComposerToolbarModels.swift +++ b/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/ComposerToolbarModels.swift @@ -6,6 +6,7 @@ // import Compound +import MatrixRustSDK import SwiftUI import WysiwygComposer @@ -283,9 +284,14 @@ extension FormatType { } enum ComposerMode: Equatable { + enum EditSource { + case timeline + case draftService + } + case `default` - case reply(itemID: TimelineItemIdentifier, replyDetails: TimelineItemReplyDetails, isThread: Bool) - case edit(originalItemId: TimelineItemIdentifier) + case reply(eventID: String, replyDetails: TimelineItemReplyDetails, isThread: Bool) + case edit(originalEventOrTransactionID: EventOrTransactionId, source: EditSource) case recordVoiceMessage(state: AudioRecorderState) case previewVoiceMessage(state: AudioPlayerState, waveform: WaveformSource, isUploading: Bool) @@ -323,8 +329,8 @@ enum ComposerMode: Equatable { var replyEventID: String? { switch self { - case .reply(let itemID, _, _): - return itemID.eventID + case .reply(let eventID, _, _): + return eventID default: return nil } diff --git a/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/ComposerToolbarViewModel.swift b/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/ComposerToolbarViewModel.swift index 970ee05be9..c15a17b65b 100644 --- a/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/ComposerToolbarViewModel.swift +++ b/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/ComposerToolbarViewModel.swift @@ -258,9 +258,9 @@ final class ComposerToolbarViewModel: ComposerToolbarViewModelType, ComposerTool case .newMessage: set(mode: .default) case .edit(let eventID): - set(mode: .edit(originalItemId: .init(uniqueID: "", eventOrTransactionID: .eventId(eventId: eventID)))) + set(mode: .edit(originalEventOrTransactionID: .eventId(eventId: eventID), source: .draftService)) case .reply(let eventID): - set(mode: .reply(itemID: .init(uniqueID: "", eventOrTransactionID: .eventId(eventId: eventID)), replyDetails: .loading(eventID: eventID), isThread: false)) + set(mode: .reply(eventID: eventID, replyDetails: .loading(eventID: eventID), isThread: false)) replyLoadingTask = Task { let reply = switch await draftService.getReply(eventID: eventID) { case .success(let reply): @@ -273,7 +273,7 @@ final class ComposerToolbarViewModel: ComposerToolbarViewModelType, ComposerTool return } - set(mode: .reply(itemID: .init(uniqueID: "", eventOrTransactionID: .eventId(eventId: eventID)), replyDetails: reply.details, isThread: reply.isThreaded)) + set(mode: .reply(eventID: eventID, replyDetails: reply.details, isThread: reply.isThreaded)) } } } @@ -314,17 +314,9 @@ final class ComposerToolbarViewModel: ComposerToolbarViewModelType, ComposerTool switch state.composerMode { case .default: type = .newMessage - case .edit(let itemID): - guard let eventID = itemID.eventID else { - MXLog.error("The event id for this message is missing") - return - } - type = .edit(eventID: eventID) - case .reply(let itemID, _, _): - guard let eventID = itemID.eventID else { - MXLog.error("The event id for this message is missing") - return - } + case .edit(.eventId(let originalEventID), _): + type = .edit(eventID: originalEventID) + case .reply(let eventID, _, _): type = .reply(eventID: eventID) default: if isVolatile { diff --git a/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/ComposerToolbar.swift b/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/ComposerToolbar.swift index c4f4fa384f..8f1dcaa6c6 100644 --- a/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/ComposerToolbar.swift +++ b/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/ComposerToolbar.swift @@ -415,10 +415,10 @@ extension ComposerToolbar { mentionDisplayHelper: ComposerMentionDisplayHelper.mock, analyticsService: ServiceLocator.shared.analytics, composerDraftService: ComposerDraftServiceMock()) - model.state.composerMode = isLoading ? .reply(itemID: .init(uniqueID: ""), + model.state.composerMode = isLoading ? .reply(eventID: UUID().uuidString, replyDetails: .loading(eventID: ""), isThread: false) : - .reply(itemID: .init(uniqueID: ""), + .reply(eventID: UUID().uuidString, replyDetails: .loaded(sender: .init(id: "", displayName: "Test"), eventID: "", eventContent: .message(.text(.init(body: "Hello World!")))), isThread: false) diff --git a/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/MessageComposer.swift b/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/MessageComposer.swift index 61a3a6911c..16f2c0f711 100644 --- a/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/MessageComposer.swift +++ b/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/MessageComposer.swift @@ -275,9 +275,9 @@ struct MessageComposer_Previews: PreviewProvider, TestablePreview { messageComposer() messageComposer(.init(string: "Some message"), - mode: .edit(originalItemId: .random)) + mode: .edit(originalEventOrTransactionID: .eventId(eventId: UUID().uuidString), source: .timeline)) - messageComposer(mode: .reply(itemID: .random, + messageComposer(mode: .reply(eventID: UUID().uuidString, replyDetails: .loaded(sender: .init(id: "Kirk"), eventID: "123", eventContent: .message(.text(.init(body: "Text: Where the wild things are")))), @@ -288,7 +288,7 @@ struct MessageComposer_Previews: PreviewProvider, TestablePreview { ScrollView { VStack(spacing: 8) { ForEach(replyTypes, id: \.self) { replyDetails in - messageComposer(mode: .reply(itemID: .random, + messageComposer(mode: .reply(eventID: UUID().uuidString, replyDetails: replyDetails, isThread: false)) } } @@ -300,7 +300,7 @@ struct MessageComposer_Previews: PreviewProvider, TestablePreview { ScrollView { VStack(spacing: 8) { ForEach(replyTypes, id: \.self) { replyDetails in - messageComposer(mode: .reply(itemID: .random, + messageComposer(mode: .reply(eventID: UUID().uuidString, replyDetails: replyDetails, isThread: true)) } } diff --git a/ElementX/Sources/Screens/Timeline/TimelineInteractionHandler.swift b/ElementX/Sources/Screens/Timeline/TimelineInteractionHandler.swift index 7696f2501a..3bab9b8db4 100644 --- a/ElementX/Sources/Screens/Timeline/TimelineInteractionHandler.swift +++ b/ElementX/Sources/Screens/Timeline/TimelineInteractionHandler.swift @@ -143,7 +143,7 @@ class TimelineInteractionHandler { let replyInfo = buildReplyInfo(for: eventTimelineItem) let replyDetails = TimelineItemReplyDetails.loaded(sender: eventTimelineItem.sender, eventID: eventID, eventContent: replyInfo.type) - actionsSubject.send(.composer(action: .setMode(mode: .reply(itemID: eventTimelineItem.id, replyDetails: replyDetails, isThread: replyInfo.isThread)))) + actionsSubject.send(.composer(action: .setMode(mode: .reply(eventID: eventID, replyDetails: replyDetails, isThread: replyInfo.isThread)))) case .forward(let itemID): actionsSubject.send(.displayMessageForwarding(itemID: itemID)) case .viewSource: @@ -184,6 +184,11 @@ class TimelineInteractionHandler { } private func processEditMessageEvent(_ messageTimelineItem: EventBasedMessageTimelineItemProtocol) { + guard case let .event(_, eventOrTransactionID) = messageTimelineItem.id else { + MXLog.error("Failed editing message, missing event id") + return + } + let text: String var htmlText: String? switch messageTimelineItem.contentType { @@ -197,7 +202,7 @@ class TimelineInteractionHandler { } // Always update the mode first and then the text so that the composer has time to save the text draft - actionsSubject.send(.composer(action: .setMode(mode: .edit(originalItemId: messageTimelineItem.id)))) + actionsSubject.send(.composer(action: .setMode(mode: .edit(originalEventOrTransactionID: eventOrTransactionID, source: .timeline)))) actionsSubject.send(.composer(action: .setText(plainText: text, htmlText: htmlText))) } diff --git a/ElementX/Sources/Screens/Timeline/TimelineViewModel.swift b/ElementX/Sources/Screens/Timeline/TimelineViewModel.swift index 2dc59055fa..bc83d1d71c 100644 --- a/ElementX/Sources/Screens/Timeline/TimelineViewModel.swift +++ b/ElementX/Sources/Screens/Timeline/TimelineViewModel.swift @@ -593,13 +593,14 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol { actionsSubject.send(.composer(action: .clear)) switch mode { - case .reply(let itemId, _, _): + case .reply(let eventID, _, _): await timelineController.sendMessage(message, html: html, - inReplyTo: itemId, + inReplyToEventID: eventID, intentionalMentions: intentionalMentions) - case .edit(let originalItemId): - await timelineController.edit(originalItemId, + case .edit(let originalEventOrTransactionID, let source): + await timelineController.edit(originalEventOrTransactionID, + useTimeline: source == .timeline, message: message, html: html, intentionalMentions: intentionalMentions) @@ -610,6 +611,7 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol { case .none: await timelineController.sendMessage(message, html: html, + inReplyToEventID: nil, intentionalMentions: intentionalMentions) } case .recordVoiceMessage, .previewVoiceMessage: diff --git a/ElementX/Sources/Screens/Timeline/View/Style/SwipeToReplyView.swift b/ElementX/Sources/Screens/Timeline/View/Style/SwipeToReplyView.swift index 47b611a66b..476ee8465f 100644 --- a/ElementX/Sources/Screens/Timeline/View/Style/SwipeToReplyView.swift +++ b/ElementX/Sources/Screens/Timeline/View/Style/SwipeToReplyView.swift @@ -19,7 +19,7 @@ struct SwipeToReplyView: View { } struct SwipeToReplyView_Previews: PreviewProvider, TestablePreview { - static let timelineItem = TextRoomTimelineItem(id: .init(uniqueID: ""), + static let timelineItem = TextRoomTimelineItem(id: .randomEvent, timestamp: "", isOutgoing: true, isEditable: true, diff --git a/ElementX/Sources/Screens/Timeline/View/Style/TimelineItemBubbledStylerView.swift b/ElementX/Sources/Screens/Timeline/View/Style/TimelineItemBubbledStylerView.swift index 7d809e2cb2..4b6eaf2e46 100644 --- a/ElementX/Sources/Screens/Timeline/View/Style/TimelineItemBubbledStylerView.swift +++ b/ElementX/Sources/Screens/Timeline/View/Style/TimelineItemBubbledStylerView.swift @@ -398,7 +398,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview static var replies: some View { VStack(spacing: 0) { - RoomTimelineItemView(viewState: .init(item: TextRoomTimelineItem(id: .init(uniqueID: ""), + RoomTimelineItemView(viewState: .init(item: TextRoomTimelineItem(id: .randomEvent, timestamp: "10:42", isOutgoing: true, isEditable: false, @@ -411,7 +411,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview eventContent: .message(.text(.init(body: "Short"))))), groupStyle: .single)) - RoomTimelineItemView(viewState: .init(item: TextRoomTimelineItem(id: .init(uniqueID: ""), + RoomTimelineItemView(viewState: .init(item: TextRoomTimelineItem(id: .randomEvent, timestamp: "10:42", isOutgoing: true, isEditable: false, @@ -443,7 +443,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview static var encryptionAuthenticity: some View { VStack(spacing: 0) { - RoomTimelineItemView(viewState: .init(item: TextRoomTimelineItem(id: .init(uniqueID: ""), + RoomTimelineItemView(viewState: .init(item: TextRoomTimelineItem(id: .randomEvent, timestamp: "10:42", isOutgoing: true, isEditable: false, @@ -454,7 +454,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview properties: RoomTimelineItemProperties(encryptionAuthenticity: .unsignedDevice(color: .red))), groupStyle: .single)) - RoomTimelineItemView(viewState: .init(item: TextRoomTimelineItem(id: .init(uniqueID: ""), + RoomTimelineItemView(viewState: .init(item: TextRoomTimelineItem(id: .randomEvent, timestamp: "10:42", isOutgoing: true, isEditable: false, @@ -466,7 +466,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview encryptionAuthenticity: .unsignedDevice(color: .red))), groupStyle: .single)) - RoomTimelineItemView(viewState: .init(item: TextRoomTimelineItem(id: .init(uniqueID: ""), + RoomTimelineItemView(viewState: .init(item: TextRoomTimelineItem(id: .randomEvent, timestamp: "10:42", isOutgoing: false, isEditable: false, @@ -477,7 +477,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview properties: RoomTimelineItemProperties(encryptionAuthenticity: .unknownDevice(color: .red))), groupStyle: .first)) - RoomTimelineItemView(viewState: .init(item: TextRoomTimelineItem(id: .init(uniqueID: ""), + RoomTimelineItemView(viewState: .init(item: TextRoomTimelineItem(id: .randomEvent, timestamp: "10:42", isOutgoing: false, isEditable: false, @@ -488,7 +488,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview properties: RoomTimelineItemProperties(encryptionAuthenticity: .notGuaranteed(color: .gray))), groupStyle: .last)) - ImageRoomTimelineView(timelineItem: ImageRoomTimelineItem(id: .random, + ImageRoomTimelineView(timelineItem: ImageRoomTimelineItem(id: .randomEvent, timestamp: "Now", isOutgoing: false, isEditable: false, @@ -501,7 +501,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview properties: RoomTimelineItemProperties(encryptionAuthenticity: .notGuaranteed(color: .gray)))) - VoiceMessageRoomTimelineView(timelineItem: .init(id: .init(uniqueID: ""), + VoiceMessageRoomTimelineView(timelineItem: .init(id: .randomEvent, timestamp: "10:42", isOutgoing: true, isEditable: false, @@ -514,7 +514,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview source: nil, contentType: nil), properties: RoomTimelineItemProperties(encryptionAuthenticity: .notGuaranteed(color: .gray))), - playerState: AudioPlayerState(id: .timelineItemIdentifier(.random), + playerState: AudioPlayerState(id: .timelineItemIdentifier(.randomEvent), title: L10n.commonVoiceMessage, duration: 10, waveform: EstimatedWaveform.mockWaveform)) @@ -616,14 +616,14 @@ private struct MockTimelineContent: View { source: nil, contentType: nil), replyDetails: replyDetails), - playerState: AudioPlayerState(id: .timelineItemIdentifier(.random), + playerState: AudioPlayerState(id: .timelineItemIdentifier(.randomEvent), title: L10n.commonVoiceMessage, duration: 10, waveform: EstimatedWaveform.mockWaveform)) } func makeItemIdentifier() -> TimelineItemIdentifier { - isPinned ? .init(uniqueID: "", eventOrTransactionID: .eventId(eventId: "pinned")) : .random + isPinned ? .event(uniqueID: "", eventOrTransactionID: .eventId(eventId: "pinned")) : .randomEvent } var replyDetails: TimelineItemReplyDetails? { diff --git a/ElementX/Sources/Screens/Timeline/View/Style/TimelineItemSendInfoLabel.swift b/ElementX/Sources/Screens/Timeline/View/Style/TimelineItemSendInfoLabel.swift index 04e5544a43..d2cbd56391 100644 --- a/ElementX/Sources/Screens/Timeline/View/Style/TimelineItemSendInfoLabel.swift +++ b/ElementX/Sources/Screens/Timeline/View/Style/TimelineItemSendInfoLabel.swift @@ -180,22 +180,22 @@ private extension EncryptionAuthenticity { struct TimelineItemSendInfoLabel_Previews: PreviewProvider, TestablePreview { static var previews: some View { VStack(spacing: 16) { - TimelineItemSendInfoLabel(sendInfo: .init(itemID: .random, + TimelineItemSendInfoLabel(sendInfo: .init(itemID: .randomEvent, localizedString: "09:47 AM", layoutType: .horizontal())) - TimelineItemSendInfoLabel(sendInfo: .init(itemID: .random, + TimelineItemSendInfoLabel(sendInfo: .init(itemID: .randomEvent, localizedString: "09:47 AM", status: .sendingFailed, layoutType: .horizontal())) - TimelineItemSendInfoLabel(sendInfo: .init(itemID: .random, + TimelineItemSendInfoLabel(sendInfo: .init(itemID: .randomEvent, localizedString: "09:47 AM", status: .encryptionAuthenticity(.unsignedDevice(color: .red)), layoutType: .horizontal())) - TimelineItemSendInfoLabel(sendInfo: .init(itemID: .random, + TimelineItemSendInfoLabel(sendInfo: .init(itemID: .randomEvent, localizedString: "09:47 AM", status: .encryptionAuthenticity(.notGuaranteed(color: .gray)), layoutType: .horizontal())) - TimelineItemSendInfoLabel(sendInfo: .init(itemID: .random, + TimelineItemSendInfoLabel(sendInfo: .init(itemID: .randomEvent, localizedString: "09:47 AM", status: .encryptionAuthenticity(.sentInClear(color: .red)), layoutType: .horizontal())) diff --git a/ElementX/Sources/Screens/Timeline/View/Style/TimelineStyler.swift b/ElementX/Sources/Screens/Timeline/View/Style/TimelineStyler.swift index ac7dc5d593..820cdd6423 100644 --- a/ElementX/Sources/Screens/Timeline/View/Style/TimelineStyler.swift +++ b/ElementX/Sources/Screens/Timeline/View/Style/TimelineStyler.swift @@ -57,7 +57,7 @@ struct TimelineStyler: View { struct TimelineItemStyler_Previews: PreviewProvider, TestablePreview { static let viewModel = TimelineViewModel.mock - static let base = TextRoomTimelineItem(id: .random, + static let base = TextRoomTimelineItem(id: .randomEvent, timestamp: "Now", isOutgoing: true, isEditable: false, @@ -80,7 +80,7 @@ struct TimelineItemStyler_Previews: PreviewProvider, TestablePreview { static let sendingLast: TextRoomTimelineItem = { let id = viewModel.state.timelineViewState.uniqueIDs.last ?? UUID().uuidString - var result = TextRoomTimelineItem(id: .init(uniqueID: id), + var result = TextRoomTimelineItem(id: .event(uniqueID: id, eventOrTransactionID: .eventId(eventId: UUID().uuidString)), timestamp: "Now", isOutgoing: true, isEditable: false, @@ -100,7 +100,7 @@ struct TimelineItemStyler_Previews: PreviewProvider, TestablePreview { static let sentLast: TextRoomTimelineItem = { let id = viewModel.state.timelineViewState.uniqueIDs.last ?? UUID().uuidString - let result = TextRoomTimelineItem(id: .init(uniqueID: id), + let result = TextRoomTimelineItem(id: .event(uniqueID: id, eventOrTransactionID: .eventId(eventId: UUID().uuidString)), timestamp: "Now", isOutgoing: true, isEditable: false, @@ -111,7 +111,7 @@ struct TimelineItemStyler_Previews: PreviewProvider, TestablePreview { return result }() - static let ltrString = TextRoomTimelineItem(id: .random, + static let ltrString = TextRoomTimelineItem(id: .randomEvent, timestamp: "Now", isOutgoing: true, isEditable: false, @@ -119,7 +119,7 @@ struct TimelineItemStyler_Previews: PreviewProvider, TestablePreview { isThreaded: false, sender: .test, content: .init(body: "house!")) - static let rtlString = TextRoomTimelineItem(id: .random, + static let rtlString = TextRoomTimelineItem(id: .randomEvent, timestamp: "Now", isOutgoing: true, isEditable: false, @@ -127,7 +127,7 @@ struct TimelineItemStyler_Previews: PreviewProvider, TestablePreview { isThreaded: false, sender: .test, content: .init(body: "באמת!")) - static let ltrStringThatContainsRtl = TextRoomTimelineItem(id: .random, + static let ltrStringThatContainsRtl = TextRoomTimelineItem(id: .randomEvent, timestamp: "Now", isOutgoing: true, isEditable: false, @@ -136,7 +136,7 @@ struct TimelineItemStyler_Previews: PreviewProvider, TestablePreview { sender: .test, content: .init(body: "house! -- באמת‏! -- house!")) - static let rtlStringThatContainsLtr = TextRoomTimelineItem(id: .random, + static let rtlStringThatContainsLtr = TextRoomTimelineItem(id: .randomEvent, timestamp: "Now", isOutgoing: true, isEditable: false, @@ -145,7 +145,7 @@ struct TimelineItemStyler_Previews: PreviewProvider, TestablePreview { sender: .test, content: .init(body: "באמת‏! -- house! -- באמת!")) - static let ltrStringThatFinishesInRtl = TextRoomTimelineItem(id: .random, + static let ltrStringThatFinishesInRtl = TextRoomTimelineItem(id: .randomEvent, timestamp: "Now", isOutgoing: true, isEditable: false, @@ -154,7 +154,7 @@ struct TimelineItemStyler_Previews: PreviewProvider, TestablePreview { sender: .test, content: .init(body: "house! -- באמת!")) - static let rtlStringThatFinishesInLtr = TextRoomTimelineItem(id: .random, + static let rtlStringThatFinishesInLtr = TextRoomTimelineItem(id: .randomEvent, timestamp: "Now", isOutgoing: true, isEditable: false, diff --git a/ElementX/Sources/Screens/Timeline/View/Supplementary/TimelineReactionsView.swift b/ElementX/Sources/Screens/Timeline/View/Supplementary/TimelineReactionsView.swift index f8656fdf23..a49631c3ba 100644 --- a/ElementX/Sources/Screens/Timeline/View/Supplementary/TimelineReactionsView.swift +++ b/ElementX/Sources/Screens/Timeline/View/Supplementary/TimelineReactionsView.swift @@ -196,20 +196,20 @@ struct TimelineReactionViewPreviewsContainer: View { var body: some View { VStack(spacing: 8) { TimelineReactionsView(context: TimelineViewModel.mock.context, - itemID: .init(uniqueID: "1"), + itemID: .randomEvent, reactions: [AggregatedReaction.mockReactionWithLongText, AggregatedReaction.mockReactionWithLongTextRTL]) Divider() TimelineReactionsView(context: TimelineViewModel.mock.context, - itemID: .init(uniqueID: "2"), + itemID: .randomEvent, reactions: Array(AggregatedReaction.mockReactions.prefix(3))) Divider() TimelineReactionsView(context: TimelineViewModel.mock.context, - itemID: .init(uniqueID: "3"), + itemID: .randomEvent, reactions: AggregatedReaction.mockReactions) Divider() TimelineReactionsView(context: TimelineViewModel.mock.context, - itemID: .init(uniqueID: "4"), + itemID: .randomEvent, reactions: AggregatedReaction.mockReactions, isLayoutRTL: true) } diff --git a/ElementX/Sources/Screens/Timeline/View/Supplementary/TimelineReadReceiptsView.swift b/ElementX/Sources/Screens/Timeline/View/Supplementary/TimelineReadReceiptsView.swift index ddf1c9b63e..f3f88fedd1 100644 --- a/ElementX/Sources/Screens/Timeline/View/Supplementary/TimelineReadReceiptsView.swift +++ b/ElementX/Sources/Screens/Timeline/View/Supplementary/TimelineReadReceiptsView.swift @@ -103,7 +103,7 @@ struct TimelineReadReceiptsView_Previews: PreviewProvider, TestablePreview { ReadReceipt(userID: RoomMemberProxyMock.mockDan.userID, formattedTimestamp: "Way, way before")] static func mockTimelineItem(with receipts: [ReadReceipt]) -> TextRoomTimelineItem { - TextRoomTimelineItem(id: .random, + TextRoomTimelineItem(id: .randomEvent, timestamp: "Now", isOutgoing: true, isEditable: false, diff --git a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/AudioRoomTimelineView.swift b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/AudioRoomTimelineView.swift index 59952d1185..b89ec72f76 100644 --- a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/AudioRoomTimelineView.swift +++ b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/AudioRoomTimelineView.swift @@ -34,7 +34,7 @@ struct AudioRoomTimelineView_Previews: PreviewProvider, TestablePreview { } static var body: some View { - AudioRoomTimelineView(timelineItem: AudioRoomTimelineItem(id: .random, + AudioRoomTimelineView(timelineItem: AudioRoomTimelineItem(id: .randomEvent, timestamp: "Now", isOutgoing: false, isEditable: false, diff --git a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/CallInviteRoomTimelineView.swift b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/CallInviteRoomTimelineView.swift index bb0406cb74..3beab6197c 100644 --- a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/CallInviteRoomTimelineView.swift +++ b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/CallInviteRoomTimelineView.swift @@ -30,7 +30,7 @@ struct CallInviteRoomTimelineView_Previews: PreviewProvider, TestablePreview { } static var body: some View { - CallInviteRoomTimelineView(timelineItem: .init(id: .random, + CallInviteRoomTimelineView(timelineItem: .init(id: .randomEvent, timestamp: "Now", isEditable: false, canBeRepliedTo: false, diff --git a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/CallNotificationRoomTimelineView.swift b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/CallNotificationRoomTimelineView.swift index f3eefcd1af..c584b8a34f 100644 --- a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/CallNotificationRoomTimelineView.swift +++ b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/CallNotificationRoomTimelineView.swift @@ -60,7 +60,7 @@ struct CallNotificationRoomTimelineView_Previews: PreviewProvider, TestablePrevi } static var body: some View { - CallNotificationRoomTimelineView(timelineItem: .init(id: .random, + CallNotificationRoomTimelineView(timelineItem: .init(id: .randomEvent, timestamp: "Now", isEditable: false, canBeRepliedTo: false, diff --git a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/CollapsibleRoomTimelineView.swift b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/CollapsibleRoomTimelineView.swift index fd50654fe1..03c212504d 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: .init(uniqueID: "First separator"), text: "This is a separator"), - SeparatorRoomTimelineItem(id: .init(uniqueID: "Second separator"), text: "This is another separator") + SeparatorRoomTimelineItem(id: .virtual(uniqueID: "First separator"), text: "This is a separator"), + SeparatorRoomTimelineItem(id: .virtual(uniqueID: "Second separator"), text: "This is another separator") ]) static var previews: some View { diff --git a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/EmoteRoomTimelineView.swift b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/EmoteRoomTimelineView.swift index 77ce3bec5a..3fd741ccd4 100644 --- a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/EmoteRoomTimelineView.swift +++ b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/EmoteRoomTimelineView.swift @@ -42,7 +42,7 @@ struct EmoteRoomTimelineView_Previews: PreviewProvider, TestablePreview { } private static func itemWith(text: String, timestamp: String, senderId: String) -> EmoteRoomTimelineItem { - EmoteRoomTimelineItem(id: .random, + EmoteRoomTimelineItem(id: .randomEvent, timestamp: timestamp, isOutgoing: false, isEditable: false, diff --git a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/EncryptedRoomTimelineView.swift b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/EncryptedRoomTimelineView.swift index 5a69efe464..ff3f5d3697 100644 --- a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/EncryptedRoomTimelineView.swift +++ b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/EncryptedRoomTimelineView.swift @@ -77,7 +77,7 @@ struct EncryptedRoomTimelineView_Previews: PreviewProvider, TestablePreview { } private static func itemWith(text: String, timestamp: String, isOutgoing: Bool, senderId: String) -> EncryptedRoomTimelineItem { - EncryptedRoomTimelineItem(id: .random, + EncryptedRoomTimelineItem(id: .randomEvent, body: text, encryptionType: .unknown, timestamp: timestamp, @@ -88,7 +88,7 @@ struct EncryptedRoomTimelineView_Previews: PreviewProvider, TestablePreview { } private static func expectedItemWith(timestamp: String, isOutgoing: Bool, senderId: String) -> EncryptedRoomTimelineItem { - EncryptedRoomTimelineItem(id: .random, + EncryptedRoomTimelineItem(id: .randomEvent, body: L10n.commonUnableToDecryptNoAccess, encryptionType: .megolmV1AesSha2(sessionID: "foo", cause: .membership), timestamp: timestamp, diff --git a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/FileRoomTimelineView.swift b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/FileRoomTimelineView.swift index a75a139b38..d9b3ccd4d5 100644 --- a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/FileRoomTimelineView.swift +++ b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/FileRoomTimelineView.swift @@ -35,7 +35,7 @@ struct FileRoomTimelineView_Previews: PreviewProvider, TestablePreview { static var body: some View { VStack(spacing: 20.0) { - FileRoomTimelineView(timelineItem: FileRoomTimelineItem(id: .random, + FileRoomTimelineView(timelineItem: FileRoomTimelineItem(id: .randomEvent, timestamp: "Now", isOutgoing: false, isEditable: false, @@ -47,7 +47,7 @@ struct FileRoomTimelineView_Previews: PreviewProvider, TestablePreview { thumbnailSource: nil, contentType: nil))) - FileRoomTimelineView(timelineItem: FileRoomTimelineItem(id: .random, + FileRoomTimelineView(timelineItem: FileRoomTimelineItem(id: .randomEvent, timestamp: "Now", isOutgoing: false, isEditable: false, @@ -59,7 +59,7 @@ struct FileRoomTimelineView_Previews: PreviewProvider, TestablePreview { thumbnailSource: nil, contentType: nil))) - FileRoomTimelineView(timelineItem: FileRoomTimelineItem(id: .random, + FileRoomTimelineView(timelineItem: FileRoomTimelineItem(id: .randomEvent, timestamp: "Now", isOutgoing: false, isEditable: false, diff --git a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/ImageRoomTimelineView.swift b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/ImageRoomTimelineView.swift index 9c8c2471a6..e86d30f13f 100644 --- a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/ImageRoomTimelineView.swift +++ b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/ImageRoomTimelineView.swift @@ -52,7 +52,7 @@ struct ImageRoomTimelineView_Previews: PreviewProvider, TestablePreview { static var body: some View { VStack(spacing: 20.0) { - ImageRoomTimelineView(timelineItem: ImageRoomTimelineItem(id: .random, + ImageRoomTimelineView(timelineItem: ImageRoomTimelineItem(id: .randomEvent, timestamp: "Now", isOutgoing: false, isEditable: false, @@ -63,7 +63,7 @@ struct ImageRoomTimelineView_Previews: PreviewProvider, TestablePreview { source: MediaSourceProxy(url: .picturesDirectory, mimeType: "image/jpg"), thumbnailSource: nil))) - ImageRoomTimelineView(timelineItem: ImageRoomTimelineItem(id: .random, + ImageRoomTimelineView(timelineItem: ImageRoomTimelineItem(id: .randomEvent, timestamp: "Now", isOutgoing: false, isEditable: false, @@ -74,7 +74,7 @@ struct ImageRoomTimelineView_Previews: PreviewProvider, TestablePreview { source: MediaSourceProxy(url: .picturesDirectory, mimeType: "image/png"), thumbnailSource: nil))) - ImageRoomTimelineView(timelineItem: ImageRoomTimelineItem(id: .random, + ImageRoomTimelineView(timelineItem: ImageRoomTimelineItem(id: .randomEvent, timestamp: "Now", isOutgoing: false, isEditable: false, diff --git a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/LocationRoomTimelineView.swift b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/LocationRoomTimelineView.swift index 8c32b487e3..6d3752dbd2 100644 --- a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/LocationRoomTimelineView.swift +++ b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/LocationRoomTimelineView.swift @@ -86,7 +86,7 @@ struct LocationRoomTimelineView_Previews: PreviewProvider, TestablePreview { @ViewBuilder static var states: some View { - LocationRoomTimelineView(timelineItem: .init(id: .random, + LocationRoomTimelineView(timelineItem: .init(id: .randomEvent, timestamp: "Now", isOutgoing: false, isEditable: false, @@ -95,7 +95,7 @@ struct LocationRoomTimelineView_Previews: PreviewProvider, TestablePreview { sender: .init(id: "Bob"), content: .init(body: "Fallback geo uri description"))) - LocationRoomTimelineView(timelineItem: .init(id: .random, + LocationRoomTimelineView(timelineItem: .init(id: .randomEvent, timestamp: "Now", isOutgoing: false, isEditable: false, @@ -104,7 +104,7 @@ struct LocationRoomTimelineView_Previews: PreviewProvider, TestablePreview { sender: .init(id: "Bob"), content: .init(body: "Fallback geo uri description", geoURI: .init(latitude: 41.902782, longitude: 12.496366), description: "Location description description description description description description description description"))) - LocationRoomTimelineView(timelineItem: .init(id: .random, + LocationRoomTimelineView(timelineItem: .init(id: .randomEvent, timestamp: "Now", isOutgoing: false, isEditable: false, diff --git a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/NoticeRoomTimelineView.swift b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/NoticeRoomTimelineView.swift index 9fe8f8fbba..098d8b93cd 100644 --- a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/NoticeRoomTimelineView.swift +++ b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/NoticeRoomTimelineView.swift @@ -54,7 +54,7 @@ struct NoticeRoomTimelineView_Previews: PreviewProvider, TestablePreview { } private static func itemWith(text: String, timestamp: String, senderId: String) -> NoticeRoomTimelineItem { - NoticeRoomTimelineItem(id: .random, + NoticeRoomTimelineItem(id: .randomEvent, timestamp: timestamp, isOutgoing: false, isEditable: false, diff --git a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/ReadMarkerRoomTimelineView.swift b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/ReadMarkerRoomTimelineView.swift index 71a3b1a0f3..8bbfaf7870 100644 --- a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/ReadMarkerRoomTimelineView.swift +++ b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/ReadMarkerRoomTimelineView.swift @@ -29,12 +29,12 @@ struct ReadMarkerRoomTimelineView: View { struct ReadMarkerRoomTimelineView_Previews: PreviewProvider, TestablePreview { static let viewModel = TimelineViewModel.mock - static let item = ReadMarkerRoomTimelineItem(id: .init(uniqueID: .init(UUID().uuidString))) + static let item = ReadMarkerRoomTimelineItem(id: .randomVirtual) static var previews: some View { VStack(alignment: .leading, spacing: 0) { - RoomTimelineItemView(viewState: .init(type: .separator(.init(id: .init(uniqueID: "Separator"), text: "Today")), groupStyle: .single)) - RoomTimelineItemView(viewState: .init(type: .text(.init(id: .random, + RoomTimelineItemView(viewState: .init(type: .separator(.init(id: .virtual(uniqueID: "Separator"), text: "Today")), groupStyle: .single)) + RoomTimelineItemView(viewState: .init(type: .text(.init(id: .randomEvent, timestamp: "", isOutgoing: true, isEditable: false, @@ -45,8 +45,8 @@ struct ReadMarkerRoomTimelineView_Previews: PreviewProvider, TestablePreview { ReadMarkerRoomTimelineView(timelineItem: item) - RoomTimelineItemView(viewState: .init(type: .separator(.init(id: .init(uniqueID: "Separator"), text: "Today")), groupStyle: .single)) - RoomTimelineItemView(viewState: .init(type: .text(.init(id: .random, + RoomTimelineItemView(viewState: .init(type: .separator(.init(id: .virtual(uniqueID: "Separator"), text: "Today")), groupStyle: .single)) + RoomTimelineItemView(viewState: .init(type: .text(.init(id: .randomEvent, timestamp: "", isOutgoing: false, isEditable: false, diff --git a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/RedactedRoomTimelineView.swift b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/RedactedRoomTimelineView.swift index 043c80b88c..81274a73f1 100644 --- a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/RedactedRoomTimelineView.swift +++ b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/RedactedRoomTimelineView.swift @@ -33,7 +33,7 @@ struct RedactedRoomTimelineView_Previews: PreviewProvider, TestablePreview { } private static func itemWith(text: String, timestamp: String, senderId: String) -> RedactedRoomTimelineItem { - RedactedRoomTimelineItem(id: .random, + RedactedRoomTimelineItem(id: .randomEvent, body: text, timestamp: timestamp, isOutgoing: false, diff --git a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/SeparatorRoomTimelineView.swift b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/SeparatorRoomTimelineView.swift index 13c5068149..3c6be1189c 100644 --- a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/SeparatorRoomTimelineView.swift +++ b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/SeparatorRoomTimelineView.swift @@ -23,7 +23,8 @@ struct SeparatorRoomTimelineView: View { struct SeparatorRoomTimelineView_Previews: PreviewProvider, TestablePreview { static var previews: some View { - let item = SeparatorRoomTimelineItem(id: .init(uniqueID: "Separator"), text: "This is a separator") + let item = SeparatorRoomTimelineItem(id: .virtual(uniqueID: "Separator"), + text: "This is a separator") SeparatorRoomTimelineView(timelineItem: item) } } diff --git a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/StateRoomTimelineView.swift b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/StateRoomTimelineView.swift index 47d1a0e055..636e89a513 100644 --- a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/StateRoomTimelineView.swift +++ b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/StateRoomTimelineView.swift @@ -30,7 +30,7 @@ struct StateRoomTimelineView_Previews: PreviewProvider, TestablePreview { StateRoomTimelineView(timelineItem: item) } - static let item = StateRoomTimelineItem(id: .random, + static let item = StateRoomTimelineItem(id: .randomVirtual, body: "Alice joined", timestamp: "Now", isOutgoing: false, diff --git a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/StickerRoomTimelineView.swift b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/StickerRoomTimelineView.swift index 2d9a17f3c8..65f155f53a 100644 --- a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/StickerRoomTimelineView.swift +++ b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/StickerRoomTimelineView.swift @@ -43,7 +43,7 @@ struct StickerRoomTimelineView_Previews: PreviewProvider, TestablePreview { static var body: some View { VStack(spacing: 20.0) { - StickerRoomTimelineView(timelineItem: StickerRoomTimelineItem(id: .random, + StickerRoomTimelineView(timelineItem: StickerRoomTimelineItem(id: .randomEvent, body: "Some image", timestamp: "Now", isOutgoing: false, @@ -52,7 +52,7 @@ struct StickerRoomTimelineView_Previews: PreviewProvider, TestablePreview { sender: .init(id: "Bob"), imageURL: URL.picturesDirectory)) - StickerRoomTimelineView(timelineItem: StickerRoomTimelineItem(id: .random, + StickerRoomTimelineView(timelineItem: StickerRoomTimelineItem(id: .randomEvent, body: "Some other image", timestamp: "Now", isOutgoing: false, @@ -61,7 +61,7 @@ struct StickerRoomTimelineView_Previews: PreviewProvider, TestablePreview { sender: .init(id: "Bob"), imageURL: URL.picturesDirectory)) - StickerRoomTimelineView(timelineItem: StickerRoomTimelineItem(id: .random, + StickerRoomTimelineView(timelineItem: StickerRoomTimelineItem(id: .randomEvent, body: "Blurhashed image", timestamp: "Now", isOutgoing: false, diff --git a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/TextRoomTimelineView.swift b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/TextRoomTimelineView.swift index 9c0b6cecac..ec853d579b 100644 --- a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/TextRoomTimelineView.swift +++ b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/TextRoomTimelineView.swift @@ -80,7 +80,7 @@ struct TextRoomTimelineView_Previews: PreviewProvider, TestablePreview { } private static func itemWith(text: String, timestamp: String, isOutgoing: Bool, senderId: String) -> TextRoomTimelineItem { - TextRoomTimelineItem(id: .random, + TextRoomTimelineItem(id: .randomEvent, timestamp: timestamp, isOutgoing: isOutgoing, isEditable: isOutgoing, @@ -94,7 +94,7 @@ struct TextRoomTimelineView_Previews: PreviewProvider, TestablePreview { let builder = AttributedStringBuilder(cacheKey: "preview", mentionBuilder: MentionBuilder()) let attributedString = builder.fromHTML(html) - return TextRoomTimelineItem(id: .random, + return TextRoomTimelineItem(id: .randomEvent, timestamp: timestamp, isOutgoing: isOutgoing, isEditable: isOutgoing, diff --git a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/UnsupportedRoomTimelineView.swift b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/UnsupportedRoomTimelineView.swift index 0f5da6dee3..2d26302ba5 100644 --- a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/UnsupportedRoomTimelineView.swift +++ b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/UnsupportedRoomTimelineView.swift @@ -52,7 +52,7 @@ struct UnsupportedRoomTimelineView_Previews: PreviewProvider, TestablePreview { } private static func itemWith(text: String, timestamp: String, isOutgoing: Bool, senderId: String) -> UnsupportedRoomTimelineItem { - UnsupportedRoomTimelineItem(id: .random, + UnsupportedRoomTimelineItem(id: .randomEvent, body: text, eventType: "Some Event Type", error: "Something went wrong", diff --git a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/VideoRoomTimelineView.swift b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/VideoRoomTimelineView.swift index fd530ff938..a99980d85b 100644 --- a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/VideoRoomTimelineView.swift +++ b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/VideoRoomTimelineView.swift @@ -63,7 +63,7 @@ struct VideoRoomTimelineView_Previews: PreviewProvider, TestablePreview { static var body: some View { VStack(spacing: 20.0) { - VideoRoomTimelineView(timelineItem: VideoRoomTimelineItem(id: .random, + VideoRoomTimelineView(timelineItem: VideoRoomTimelineItem(id: .randomEvent, timestamp: "Now", isOutgoing: false, isEditable: false, @@ -75,7 +75,7 @@ struct VideoRoomTimelineView_Previews: PreviewProvider, TestablePreview { source: nil, thumbnailSource: nil))) - VideoRoomTimelineView(timelineItem: VideoRoomTimelineItem(id: .random, + VideoRoomTimelineView(timelineItem: VideoRoomTimelineItem(id: .randomEvent, timestamp: "Now", isOutgoing: false, isEditable: false, @@ -87,7 +87,7 @@ struct VideoRoomTimelineView_Previews: PreviewProvider, TestablePreview { source: nil, thumbnailSource: nil))) - VideoRoomTimelineView(timelineItem: VideoRoomTimelineItem(id: .random, + VideoRoomTimelineView(timelineItem: VideoRoomTimelineItem(id: .randomEvent, timestamp: "Now", isOutgoing: false, isEditable: false, diff --git a/ElementX/Sources/Services/Timeline/Fixtures/RoomTimelineItemFixtures.swift b/ElementX/Sources/Services/Timeline/Fixtures/RoomTimelineItemFixtures.swift index ff84de4355..dda6752769 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: .init(uniqueID: "Yesterday"), text: "Yesterday"), - TextRoomTimelineItem(id: .init(uniqueID: ".RoomTimelineItemFixtures.default.0", - eventOrTransactionID: .eventId(eventId: "RoomTimelineItemFixtures.default.0")), + SeparatorRoomTimelineItem(id: .virtual(uniqueID: "Yesterday"), text: "Yesterday"), + TextRoomTimelineItem(id: .event(uniqueID: ".RoomTimelineItemFixtures.default.0", + eventOrTransactionID: .eventId(eventId: "RoomTimelineItemFixtures.default.0")), timestamp: "10:10 AM", 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: .init(uniqueID: "RoomTimelineItemFixtures.default.1", - eventOrTransactionID: .eventId(eventId: "RoomTimelineItemFixtures.default.1")), + TextRoomTimelineItem(id: .event(uniqueID: "RoomTimelineItemFixtures.default.1", + eventOrTransactionID: .eventId(eventId: "RoomTimelineItemFixtures.default.1")), timestamp: "10:11 AM", 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: .init(uniqueID: "RoomTimelineItemFixtures.default.2", - eventOrTransactionID: .eventId(eventId: "RoomTimelineItemFixtures.default.2")), + TextRoomTimelineItem(id: .event(uniqueID: "RoomTimelineItemFixtures.default.2", + eventOrTransactionID: .eventId(eventId: "RoomTimelineItemFixtures.default.2")), timestamp: "10:11 AM", isOutgoing: false, isEditable: false, @@ -52,9 +52,9 @@ enum RoomTimelineItemFixtures { ReactionSender(id: "jacob", timestamp: Date()) ]) ])), - SeparatorRoomTimelineItem(id: .init(uniqueID: "Today"), text: "Today"), - TextRoomTimelineItem(id: .init(uniqueID: "RoomTimelineItemFixtures.default.3", - eventOrTransactionID: .eventId(eventId: "RoomTimelineItemFixtures.default.3")), + SeparatorRoomTimelineItem(id: .virtual(uniqueID: "Today"), text: "Today"), + TextRoomTimelineItem(id: .event(uniqueID: "RoomTimelineItemFixtures.default.3", + eventOrTransactionID: .eventId(eventId: "RoomTimelineItemFixtures.default.3")), timestamp: "5 PM", 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: .init(uniqueID: "RoomTimelineItemFixtures.default.4", - eventOrTransactionID: .eventId(eventId: "RoomTimelineItemFixtures.default.4")), + TextRoomTimelineItem(id: .event(uniqueID: "RoomTimelineItemFixtures.default.4", + eventOrTransactionID: .eventId(eventId: "RoomTimelineItemFixtures.default.4")), timestamp: "5 PM", 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: .init(uniqueID: "RoomTimelineItemFixtures.default.5", - eventOrTransactionID: .eventId(eventId: "RoomTimelineItemFixtures.default.5")), + TextRoomTimelineItem(id: .event(uniqueID: "RoomTimelineItemFixtures.default.5", + eventOrTransactionID: .eventId(eventId: "RoomTimelineItemFixtures.default.5")), timestamp: "5 PM", 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: .init(uniqueID: "RoomTimelineItemFixtures.default.6", - eventOrTransactionID: .eventId(eventId: "RoomTimelineItemFixtures.default.6")), + TextRoomTimelineItem(id: .event(uniqueID: "RoomTimelineItemFixtures.default.6", + eventOrTransactionID: .eventId(eventId: "RoomTimelineItemFixtures.default.6")), timestamp: "5 PM", isOutgoing: false, isEditable: false, @@ -242,7 +242,7 @@ enum RoomTimelineItemFixtures { static var permalinkChunk: [RoomTimelineItemProtocol] { (1...20).map { index in - TextRoomTimelineItem(id: .init(uniqueID: "\(index)", eventOrTransactionID: .eventId(eventId: "$\(index)")), + TextRoomTimelineItem(id: .event(uniqueID: "\(index)", eventOrTransactionID: .eventId(eventId: "$\(index)")), text: "Message ID \(index)", senderDisplayName: index > 10 ? "Alice" : "Bob") } @@ -250,7 +250,7 @@ enum RoomTimelineItemFixtures { static var mediaChunk: [RoomTimelineItemProtocol] { [ - VideoRoomTimelineItem(id: .random, + VideoRoomTimelineItem(id: .randomEvent, timestamp: "10:47 am", isOutgoing: false, isEditable: false, @@ -265,7 +265,7 @@ enum RoomTimelineItemFixtures { height: 1080, aspectRatio: 1.78, blurhash: "KtI~70X5V?yss9oyrYs:t6")), - ImageRoomTimelineItem(id: .random, + ImageRoomTimelineItem(id: .randomEvent, timestamp: "10:47 am", isOutgoing: false, isEditable: false, @@ -285,7 +285,7 @@ enum RoomTimelineItemFixtures { private extension TextRoomTimelineItem { init(id: TimelineItemIdentifier? = nil, text: String, senderDisplayName: String) { - self.init(id: id ?? .random, + self.init(id: id ?? .randomEvent, timestamp: "10:47 am", isOutgoing: senderDisplayName == "Alice", isEditable: false, diff --git a/ElementX/Sources/Services/Timeline/TimelineController/MockRoomTimelineController.swift b/ElementX/Sources/Services/Timeline/TimelineController/MockRoomTimelineController.swift index d4844c8810..155989b151 100644 --- a/ElementX/Sources/Services/Timeline/TimelineController/MockRoomTimelineController.swift +++ b/ElementX/Sources/Services/Timeline/TimelineController/MockRoomTimelineController.swift @@ -81,12 +81,13 @@ class MockRoomTimelineController: RoomTimelineControllerProtocol { func sendMessage(_ message: String, html: String?, - inReplyTo itemID: TimelineItemIdentifier?, + inReplyToEventID: String?, intentionalMentions: IntentionalMentions) async { } func toggleReaction(_ reaction: String, to itemID: TimelineItemIdentifier) async { } - func edit(_ timelineItemID: TimelineItemIdentifier, + func edit(_ eventOrTransactionID: EventOrTransactionId, + useTimeline: Bool, message: String, html: String?, intentionalMentions: IntentionalMentions) async { } diff --git a/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineController.swift b/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineController.swift index cf0381f3a6..0ef0dbd1d6 100644 --- a/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineController.swift +++ b/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineController.swift @@ -143,22 +143,13 @@ class RoomTimelineController: RoomTimelineControllerProtocol { func sendMessage(_ message: String, html: String?, - inReplyTo itemID: TimelineItemIdentifier?, + inReplyToEventID: String?, intentionalMentions: IntentionalMentions) async { - var inReplyTo: String? - if itemID == nil { - MXLog.info("Send message in \(roomID)") - } 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 - } + MXLog.info("Send message in \(roomID)") switch await activeTimeline.sendMessage(message, html: html, - inReplyTo: inReplyTo, + inReplyToEventID: inReplyToEventID, intentionalMentions: intentionalMentions) { case .success: MXLog.info("Finished sending message") @@ -178,37 +169,31 @@ class RoomTimelineController: RoomTimelineControllerProtocol { } } - func edit(_ timelineItemID: TimelineItemIdentifier, + func edit(_ eventOrTransactionID: EventOrTransactionId, + useTimeline: Bool, message: String, html: String?, intentionalMentions: IntentionalMentions) async { MXLog.info("Edit message in \(roomID)") - MXLog.info("Editing timeline item: \(timelineItemID)") - - let editMode: EditMode - if !timelineItemID.uniqueID.isEmpty, - let timelineItem = liveTimelineProvider.itemProxies.firstEventTimelineItemUsingStableID(timelineItemID) { - editMode = .byEvent(timelineItem) - } else if let eventID = timelineItemID.eventID { - editMode = .byID(eventID) - } else { - MXLog.error("Unknown timeline item: \(timelineItemID)") - return - } + MXLog.info("Editing timeline item: \(eventOrTransactionID)") let messageContent = activeTimeline.buildMessageContentFor(message, html: html, intentionalMentions: intentionalMentions.toRustMentions()) - switch editMode { - case let .byEvent(item): - switch await activeTimeline.edit(item, newContent: messageContent) { + if useTimeline { + switch await activeTimeline.edit(eventOrTransactionID, newContent: messageContent) { case .success: MXLog.info("Finished editing message by event") case let .failure(error): MXLog.error("Failed editing message by event with error: \(error)") } - case let .byID(eventID): + } else { + guard case let .eventId(eventID) = eventOrTransactionID else { + MXLog.error("Failed editing message, missing eventID.") + return + } + switch await roomProxy.edit(eventID: eventID, newContent: messageContent) { case .success: MXLog.info("Finished editing message by event ID") @@ -398,9 +383,9 @@ class RoomTimelineController: RoomTimelineControllerProtocol { let date = Date(timeIntervalSince1970: TimeInterval(timestamp / 1000)) let dateString = date.formatted(date: .complete, time: .omitted) - return SeparatorRoomTimelineItem(id: .init(uniqueID: dateString), text: dateString) + return SeparatorRoomTimelineItem(id: .virtual(uniqueID: uniqueID), text: dateString) case .readMarker: - return ReadMarkerRoomTimelineItem(id: .init(uniqueID: uniqueID)) + return ReadMarkerRoomTimelineItem(id: .virtual(uniqueID: uniqueID)) } case .unknown: return nil @@ -453,8 +438,3 @@ class RoomTimelineController: RoomTimelineControllerProtocol { return nil } } - -private enum EditMode { - case byEvent(EventTimelineItem) - case byID(String) -} diff --git a/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineControllerProtocol.swift b/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineControllerProtocol.swift index 4ea6719dde..be7c635585 100644 --- a/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineControllerProtocol.swift +++ b/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineControllerProtocol.swift @@ -48,10 +48,11 @@ protocol RoomTimelineControllerProtocol { func sendMessage(_ message: String, html: String?, - inReplyTo itemID: TimelineItemIdentifier?, + inReplyToEventID: String?, intentionalMentions: IntentionalMentions) async - func edit(_ timelineItemID: TimelineItemIdentifier, + func edit(_ eventOrTransactionID: EventOrTransactionId, + useTimeline: Bool, message: String, html: String?, intentionalMentions: IntentionalMentions) async @@ -72,14 +73,3 @@ protocol RoomTimelineControllerProtocol { func eventTimestamp(for itemID: TimelineItemIdentifier) -> Date? } - -extension RoomTimelineControllerProtocol { - func sendMessage(_ message: String, - html: String?, - intentionalMentions: IntentionalMentions) async { - await sendMessage(message, - html: html, - inReplyTo: nil, - intentionalMentions: intentionalMentions) - } -} diff --git a/ElementX/Sources/Services/Timeline/TimelineItemIdentifier.swift b/ElementX/Sources/Services/Timeline/TimelineItemIdentifier.swift new file mode 100644 index 0000000000..1770dbb493 --- /dev/null +++ b/ElementX/Sources/Services/Timeline/TimelineItemIdentifier.swift @@ -0,0 +1,66 @@ +// +// Copyright 2024 New Vector Ltd. +// +// SPDX-License-Identifier: AGPL-3.0-only +// Please see LICENSE in the repository root for full details. +// + +import Foundation +import MatrixRustSDK + +/// A timeline item identifier +/// - uniqueID: Stable id across state changes of the timeline item, it uniquely identifies an item in a timeline. +/// Its value is consistent only per timeline instance, it should **not** be used to identify an item across timeline instances. +/// - 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 { + case event(uniqueID: String, eventOrTransactionID: EventOrTransactionId) + case virtual(uniqueID: String) + + var uniqueID: String { + switch self { + case .event(let uniqueID, _): + return uniqueID + case .virtual(let uniqueID): + return uniqueID + } + } + + var eventID: String? { + guard case let .event(_, eventOrTransactionID) = self else { + return nil + } + + switch eventOrTransactionID { + case .eventId(let eventID): + return eventID + default: + return nil + } + } + + var transactionID: String? { + guard case let .event(_, eventOrTransactionID) = self else { + return nil + } + + switch eventOrTransactionID { + case .transactionId(let transactionID): + return transactionID + default: + return nil + } + } +} + +// MARK: - Mocks + +extension TimelineItemIdentifier { + static var randomEvent: Self { + .event(uniqueID: UUID().uuidString, eventOrTransactionID: .eventId(eventId: UUID().uuidString)) + } + + static var randomVirtual: Self { + .virtual(uniqueID: UUID().uuidString) + } +} diff --git a/ElementX/Sources/Services/Timeline/TimelineItemProxy.swift b/ElementX/Sources/Services/Timeline/TimelineItemProxy.swift index f2b84499a4..20abbdb246 100644 --- a/ElementX/Sources/Services/Timeline/TimelineItemProxy.swift +++ b/ElementX/Sources/Services/Timeline/TimelineItemProxy.swift @@ -8,40 +8,6 @@ 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 uniqueID: String - - /// Contains the 2 possible identifiers of an event, either it has a remote event id or a local transaction id, never both or none. - var eventOrTransactionID: EventOrTransactionId? - - var eventID: String? { - switch eventOrTransactionID { - case .eventId(let eventId): - return eventId - default: - return nil - } - } - - var transactionID: String? { - switch eventOrTransactionID { - case .transactionId(let transactionId): - return transactionId - default: - return nil - } - } -} - -extension TimelineItemIdentifier { - /// Use only for mocks/tests - static var random: Self { - .init(uniqueID: UUID().uuidString, eventOrTransactionID: .eventId(eventId: UUID().uuidString)) - } -} - /// A light wrapper around timeline items returned from Rust. enum TimelineItemProxy { case event(EventTimelineItemProxy) @@ -108,7 +74,7 @@ class EventTimelineItemProxy { init(item: MatrixRustSDK.EventTimelineItem, uniqueID: String) { self.item = item - id = TimelineItemIdentifier(uniqueID: uniqueID, eventOrTransactionID: item.eventOrTransactionId) + id = .event(uniqueID: uniqueID, eventOrTransactionID: item.eventOrTransactionId) } lazy var deliveryStatus: TimelineItemDeliveryStatus? = { diff --git a/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VoiceMessages/VoiceMessageRoomPlaybackView.swift b/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VoiceMessages/VoiceMessageRoomPlaybackView.swift index 0b5d3354b3..1cda7b3bda 100644 --- a/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VoiceMessages/VoiceMessageRoomPlaybackView.swift +++ b/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VoiceMessages/VoiceMessageRoomPlaybackView.swift @@ -97,7 +97,7 @@ struct VoiceMessageRoomPlaybackView_Previews: PreviewProvider, TestablePreview { 294, 131, 19, 2, 3, 3, 1, 2, 0, 0, 0, 0, 0, 0, 0, 3]) - static var playerState = AudioPlayerState(id: .timelineItemIdentifier(.random), + static var playerState = AudioPlayerState(id: .timelineItemIdentifier(.randomEvent), title: L10n.commonVoiceMessage, duration: 10.0, waveform: waveform, diff --git a/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VoiceMessages/VoiceMessageRoomTimelineView.swift b/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VoiceMessages/VoiceMessageRoomTimelineView.swift index 06ca0a137c..0e70287ac5 100644 --- a/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VoiceMessages/VoiceMessageRoomTimelineView.swift +++ b/ElementX/Sources/Services/Timeline/TimelineItems/Items/Messages/VoiceMessages/VoiceMessageRoomTimelineView.swift @@ -55,7 +55,7 @@ struct VoiceMessageRoomTimelineView: View { struct VoiceMessageRoomTimelineView_Previews: PreviewProvider, TestablePreview { static let viewModel = TimelineViewModel.mock - static let timelineItemIdentifier = TimelineItemIdentifier.random + static let timelineItemIdentifier = TimelineItemIdentifier.randomEvent static let voiceRoomTimelineItem = VoiceMessageRoomTimelineItem(id: timelineItemIdentifier, timestamp: "Now", isOutgoing: false, diff --git a/ElementX/Sources/Services/Timeline/TimelineItems/Items/Virtual/PaginationIndicatorRoomTimelineItem.swift b/ElementX/Sources/Services/Timeline/TimelineItems/Items/Virtual/PaginationIndicatorRoomTimelineItem.swift index 837798cb49..319d024476 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 = TimelineItemIdentifier(uniqueID: position.id) + id = .virtual(uniqueID: 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 17435d9cce..33a601445f 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(uniqueID: UUID().uuidString) + let id: TimelineItemIdentifier = .virtual(uniqueID: UUID().uuidString) let name: String? } diff --git a/ElementX/Sources/Services/Timeline/TimelineProxy.swift b/ElementX/Sources/Services/Timeline/TimelineProxy.swift index 3d7c7ee806..74fe851ae4 100644 --- a/ElementX/Sources/Services/Timeline/TimelineProxy.swift +++ b/ElementX/Sources/Services/Timeline/TimelineProxy.swift @@ -164,17 +164,17 @@ final class TimelineProxy: TimelineProxyProtocol { } } - func edit(_ timelineItem: EventTimelineItem, newContent: RoomMessageEventContentWithoutRelation) async -> Result { + func edit(_ eventOrTransactionID: EventOrTransactionId, newContent: RoomMessageEventContentWithoutRelation) async -> Result { do { - guard try await timeline.edit(eventOrTransactionId: timelineItem.eventOrTransactionId, newContent: .roomMessage(content: newContent)) == true else { + guard try await timeline.edit(eventOrTransactionId: eventOrTransactionID, newContent: .roomMessage(content: newContent)) == true else { return .failure(.failedEditing) } - MXLog.info("Finished editing timeline item: \(timelineItem.eventOrTransactionId)") + MXLog.info("Finished editing timeline item: \(eventOrTransactionID)") return .success(()) } catch { - MXLog.error("Failed editing timeline item: \(timelineItem.eventOrTransactionId) with error: \(error)") + MXLog.error("Failed editing timeline item: \(eventOrTransactionID) with error: \(error)") return .failure(.sdkError(error)) } } @@ -366,10 +366,10 @@ final class TimelineProxy: TimelineProxyProtocol { func sendMessage(_ message: String, html: String?, - inReplyTo eventID: String? = nil, + inReplyToEventID: String? = nil, intentionalMentions: IntentionalMentions) async -> Result { - if let eventID { - MXLog.info("Sending reply to eventID: \(eventID)") + if let inReplyToEventID { + MXLog.info("Sending reply to eventID: \(inReplyToEventID)") } else { MXLog.info("Sending message") } @@ -379,16 +379,16 @@ final class TimelineProxy: TimelineProxyProtocol { intentionalMentions: intentionalMentions.toRustMentions()) do { - if let eventID { - try await timeline.sendReply(msg: messageContent, eventId: eventID) - MXLog.info("Finished sending reply to eventID: \(eventID)") + if let inReplyToEventID { + try await timeline.sendReply(msg: messageContent, eventId: inReplyToEventID) + MXLog.info("Finished sending reply to eventID: \(inReplyToEventID)") } else { _ = try await timeline.send(msg: messageContent) MXLog.info("Finished sending message") } } catch { - if let eventID { - MXLog.error("Failed sending reply to eventID: \(eventID) with error: \(error)") + if let inReplyToEventID { + MXLog.error("Failed sending reply to eventID: \(inReplyToEventID) with error: \(error)") } else { MXLog.error("Failed sending message with error: \(error)") } @@ -631,4 +631,16 @@ extension Array where Element == TimelineItemProxy { return nil } + + func firstEventTimelineItemUsingEventOrTransactionID(_ eventOrTransactionID: EventOrTransactionId) -> EventTimelineItem? { + for item in self { + if case let .event(eventTimelineItem) = item, + case let .event(_, identifier) = eventTimelineItem.id, + identifier == eventOrTransactionID { + return eventTimelineItem.item + } + } + + return nil + } } diff --git a/ElementX/Sources/Services/Timeline/TimelineProxyProtocol.swift b/ElementX/Sources/Services/Timeline/TimelineProxyProtocol.swift index c081388487..0f18fa5db0 100644 --- a/ElementX/Sources/Services/Timeline/TimelineProxyProtocol.swift +++ b/ElementX/Sources/Services/Timeline/TimelineProxyProtocol.swift @@ -38,7 +38,8 @@ protocol TimelineProxyProtocol { func paginateBackwards(requestSize: UInt16) async -> Result func paginateForwards(requestSize: UInt16) async -> Result - func edit(_ timelineItem: EventTimelineItem, newContent: RoomMessageEventContentWithoutRelation) async -> Result + func edit(_ eventOrTransactionID: EventOrTransactionId, + newContent: RoomMessageEventContentWithoutRelation) async -> Result func redact(_ timelineItemID: TimelineItemIdentifier, reason: String?) async -> Result @@ -89,12 +90,11 @@ protocol TimelineProxyProtocol { func sendMessage(_ message: String, html: String?, - inReplyTo eventID: String?, + inReplyToEventID: String?, intentionalMentions: IntentionalMentions) async -> Result func toggleReaction(_ reaction: String, to itemID: TimelineItemIdentifier) async -> Result - // Polls func createPoll(question: String, answers: [String], pollKind: Poll.Kind) async -> Result func editPoll(original eventID: String, @@ -112,14 +112,3 @@ protocol TimelineProxyProtocol { html: String?, intentionalMentions: Mentions) -> RoomMessageEventContentWithoutRelation } - -extension TimelineProxyProtocol { - func sendMessage(_ message: String, - html: String?, - intentionalMentions: IntentionalMentions) async -> Result { - await sendMessage(message, - html: html, - inReplyTo: nil, - intentionalMentions: intentionalMentions) - } -} diff --git a/UnitTests/Sources/AudioPlayerStateTests.swift b/UnitTests/Sources/AudioPlayerStateTests.swift index 7761de3a2c..469dca9bf8 100644 --- a/UnitTests/Sources/AudioPlayerStateTests.swift +++ b/UnitTests/Sources/AudioPlayerStateTests.swift @@ -38,7 +38,7 @@ class AudioPlayerStateTests: XCTestCase { override func setUp() async throws { audioPlayerActionsSubject = .init() audioPlayerSeekCallsSubject = .init() - audioPlayerState = AudioPlayerState(id: .timelineItemIdentifier(.random), title: "", duration: Self.audioDuration) + audioPlayerState = AudioPlayerState(id: .timelineItemIdentifier(.randomEvent), title: "", duration: Self.audioDuration) audioPlayerMock = buildAudioPlayerMock() audioPlayerMock.seekToClosure = { [weak self] progress in self?.audioPlayerMock.currentTime = Self.audioDuration * progress @@ -162,7 +162,7 @@ class AudioPlayerStateTests: XCTestCase { func testHandlingAudioPlayerActionDidFinishLoading() async throws { audioPlayerMock.duration = 10.0 - audioPlayerState = AudioPlayerState(id: .timelineItemIdentifier(.random), title: "", duration: 0) + audioPlayerState = AudioPlayerState(id: .timelineItemIdentifier(.randomEvent), title: "", duration: 0) audioPlayerState.attachAudioPlayer(audioPlayerMock) let deferred = deferFulfillment(audioPlayerState.$playbackState) { action in diff --git a/UnitTests/Sources/ComposerToolbarViewModelTests.swift b/UnitTests/Sources/ComposerToolbarViewModelTests.swift index f869343d32..49aa528bfa 100644 --- a/UnitTests/Sources/ComposerToolbarViewModelTests.swift +++ b/UnitTests/Sources/ComposerToolbarViewModelTests.swift @@ -41,14 +41,14 @@ class ComposerToolbarViewModelTests: XCTestCase { } func testComposerFocus() { - viewModel.process(timelineAction: .setMode(mode: .edit(originalItemId: TimelineItemIdentifier(uniqueID: "mock")))) + viewModel.process(timelineAction: .setMode(mode: .edit(originalEventOrTransactionID: .eventId(eventId: "mock"), source: .timeline))) XCTAssertTrue(viewModel.state.bindings.composerFocused) viewModel.process(timelineAction: .removeFocus) XCTAssertFalse(viewModel.state.bindings.composerFocused) } func testComposerMode() { - let mode: ComposerMode = .edit(originalItemId: TimelineItemIdentifier(uniqueID: "mock")) + let mode: ComposerMode = .edit(originalEventOrTransactionID: .eventId(eventId: "mock"), source: .timeline) viewModel.process(timelineAction: .setMode(mode: mode)) XCTAssertEqual(viewModel.state.composerMode, mode) viewModel.process(timelineAction: .clear) @@ -56,7 +56,7 @@ class ComposerToolbarViewModelTests: XCTestCase { } func testComposerModeIsPublished() { - let mode: ComposerMode = .edit(originalItemId: TimelineItemIdentifier(uniqueID: "mock")) + let mode: ComposerMode = .edit(originalEventOrTransactionID: .eventId(eventId: "mock"), source: .timeline) let expectation = expectation(description: "Composer mode is published") let cancellable = viewModel .context @@ -236,7 +236,7 @@ class ComposerToolbarViewModelTests: XCTestCase { } viewModel.context.composerFormattingEnabled = false - viewModel.process(timelineAction: .setMode(mode: .edit(originalItemId: .init(uniqueID: "", eventOrTransactionID: .eventId(eventId: "testID"))))) + viewModel.process(timelineAction: .setMode(mode: .edit(originalEventOrTransactionID: .eventId(eventId: "testID"), source: .timeline))) viewModel.context.plainComposerText = .init(string: "Hello world!") viewModel.saveDraft() @@ -257,7 +257,7 @@ class ComposerToolbarViewModelTests: XCTestCase { } viewModel.context.composerFormattingEnabled = false - viewModel.process(timelineAction: .setMode(mode: .reply(itemID: .init(uniqueID: "", eventOrTransactionID: .eventId(eventId: "testID")), + viewModel.process(timelineAction: .setMode(mode: .reply(eventID: "testID", replyDetails: .loaded(sender: .init(id: ""), eventID: "testID", eventContent: .message(.text(.init(body: "reply text")))), @@ -282,7 +282,7 @@ class ComposerToolbarViewModelTests: XCTestCase { } viewModel.context.composerFormattingEnabled = false - viewModel.process(timelineAction: .setMode(mode: .reply(itemID: .init(uniqueID: "", eventOrTransactionID: .eventId(eventId: "testID")), + viewModel.process(timelineAction: .setMode(mode: .reply(eventID: "testID", replyDetails: .loaded(sender: .init(id: ""), eventID: "testID", eventContent: .message(.text(.init(body: "reply text")))), @@ -395,7 +395,7 @@ class ComposerToolbarViewModelTests: XCTestCase { await fulfillment(of: [expectation], timeout: 10) XCTAssertFalse(viewModel.context.composerFormattingEnabled) - XCTAssertEqual(viewModel.state.composerMode, .edit(originalItemId: .init(uniqueID: "", eventOrTransactionID: .eventId(eventId: "testID")))) + XCTAssertEqual(viewModel.state.composerMode, .edit(originalEventOrTransactionID: .eventId(eventId: "testID"), source: .draftService)) XCTAssertEqual(viewModel.context.plainComposerText, NSAttributedString(string: "Hello world!")) } @@ -429,13 +429,13 @@ class ComposerToolbarViewModelTests: XCTestCase { await fulfillment(of: [draftExpectation], timeout: 10) XCTAssertFalse(viewModel.context.composerFormattingEnabled) // Testing the loading state first - XCTAssertEqual(viewModel.state.composerMode, .reply(itemID: .init(uniqueID: "", eventOrTransactionID: .eventId(eventId: testEventID)), + XCTAssertEqual(viewModel.state.composerMode, .reply(eventID: testEventID, replyDetails: .loading(eventID: testEventID), isThread: false)) XCTAssertEqual(viewModel.context.plainComposerText, NSAttributedString(string: text)) await fulfillment(of: [loadReplyExpectation], timeout: 10) - XCTAssertEqual(viewModel.state.composerMode, .reply(itemID: .init(uniqueID: "", eventOrTransactionID: .eventId(eventId: testEventID)), + XCTAssertEqual(viewModel.state.composerMode, .reply(eventID: testEventID, replyDetails: loadedReply, isThread: true)) } @@ -469,7 +469,7 @@ class ComposerToolbarViewModelTests: XCTestCase { await fulfillment(of: [draftExpectation], timeout: 10) XCTAssertFalse(viewModel.context.composerFormattingEnabled) // Testing the loading state first - XCTAssertEqual(viewModel.state.composerMode, .reply(itemID: .init(uniqueID: "", eventOrTransactionID: .eventId(eventId: testEventID)), + XCTAssertEqual(viewModel.state.composerMode, .reply(eventID: testEventID, replyDetails: .loading(eventID: testEventID), isThread: false)) XCTAssertEqual(viewModel.context.plainComposerText, NSAttributedString(string: text)) @@ -483,7 +483,7 @@ class ComposerToolbarViewModelTests: XCTestCase { func testSaveVolatileDraftWhenEditing() { viewModel.context.composerFormattingEnabled = false viewModel.context.plainComposerText = .init(string: "Hello world!") - viewModel.process(timelineAction: .setMode(mode: .edit(originalItemId: .random))) + viewModel.process(timelineAction: .setMode(mode: .edit(originalEventOrTransactionID: .eventId(eventId: UUID().uuidString), source: .timeline))) let draft = draftServiceMock.saveVolatileDraftReceivedDraft XCTAssertNotNil(draft) diff --git a/UnitTests/Sources/LoggingTests.swift b/UnitTests/Sources/LoggingTests.swift index 9b22fd2660..6887809a5a 100644 --- a/UnitTests/Sources/LoggingTests.swift +++ b/UnitTests/Sources/LoggingTests.swift @@ -116,7 +116,7 @@ class LoggingTests: XCTestCase { func validateTimelineContentIsRedacted() throws { // Given timeline items that contain text let textAttributedString = "TextAttributed" - let textMessage = TextRoomTimelineItem(id: .random, + let textMessage = TextRoomTimelineItem(id: .randomEvent, timestamp: "", isOutgoing: false, isEditable: false, @@ -125,7 +125,7 @@ class LoggingTests: XCTestCase { sender: .init(id: "sender"), content: .init(body: "TextString", formattedBody: AttributedString(textAttributedString))) let noticeAttributedString = "NoticeAttributed" - let noticeMessage = NoticeRoomTimelineItem(id: .random, + let noticeMessage = NoticeRoomTimelineItem(id: .randomEvent, timestamp: "", isOutgoing: false, isEditable: false, @@ -134,7 +134,7 @@ class LoggingTests: XCTestCase { sender: .init(id: "sender"), content: .init(body: "NoticeString", formattedBody: AttributedString(noticeAttributedString))) let emoteAttributedString = "EmoteAttributed" - let emoteMessage = EmoteRoomTimelineItem(id: .random, + let emoteMessage = EmoteRoomTimelineItem(id: .randomEvent, timestamp: "", isOutgoing: false, isEditable: false, @@ -142,7 +142,7 @@ class LoggingTests: XCTestCase { isThreaded: false, sender: .init(id: "sender"), content: .init(body: "EmoteString", formattedBody: AttributedString(emoteAttributedString))) - let imageMessage = ImageRoomTimelineItem(id: .init(uniqueID: "myimagemessage"), + let imageMessage = ImageRoomTimelineItem(id: .randomEvent, timestamp: "", isOutgoing: false, isEditable: false, @@ -153,7 +153,7 @@ class LoggingTests: XCTestCase { caption: "ImageString", source: MediaSourceProxy(url: .picturesDirectory, mimeType: "image/gif"), thumbnailSource: nil)) - let videoMessage = VideoRoomTimelineItem(id: .random, + let videoMessage = VideoRoomTimelineItem(id: .randomEvent, timestamp: "", isOutgoing: false, isEditable: false, @@ -165,7 +165,7 @@ class LoggingTests: XCTestCase { duration: 0, source: nil, thumbnailSource: nil)) - let fileMessage = FileRoomTimelineItem(id: .random, + let fileMessage = FileRoomTimelineItem(id: .randomEvent, timestamp: "", isOutgoing: false, isEditable: false, diff --git a/UnitTests/Sources/MediaPlayerProviderTests.swift b/UnitTests/Sources/MediaPlayerProviderTests.swift index a9fcca9a6a..92ef5997d5 100644 --- a/UnitTests/Sources/MediaPlayerProviderTests.swift +++ b/UnitTests/Sources/MediaPlayerProviderTests.swift @@ -60,7 +60,7 @@ class MediaPlayerProviderTests: XCTestCase { } func testPlayerStates() async throws { - let audioPlayerStateId = AudioPlayerStateIdentifier.timelineItemIdentifier(.random) + let audioPlayerStateId = AudioPlayerStateIdentifier.timelineItemIdentifier(.randomEvent) // By default, there should be no player state XCTAssertNil(mediaPlayerProvider.playerState(for: audioPlayerStateId)) @@ -76,7 +76,7 @@ class MediaPlayerProviderTests: XCTestCase { let audioPlayer = AudioPlayerMock() audioPlayer.actions = PassthroughSubject().eraseToAnyPublisher() - let audioPlayerStates = Array(repeating: AudioPlayerState(id: .timelineItemIdentifier(.random), title: "", duration: 0), count: 10) + let audioPlayerStates = Array(repeating: AudioPlayerState(id: .timelineItemIdentifier(.randomEvent), title: "", duration: 0), count: 10) for audioPlayerState in audioPlayerStates { mediaPlayerProvider.register(audioPlayerState: audioPlayerState) audioPlayerState.attachAudioPlayer(audioPlayer) @@ -95,7 +95,7 @@ class MediaPlayerProviderTests: XCTestCase { let audioPlayer = AudioPlayerMock() audioPlayer.actions = PassthroughSubject().eraseToAnyPublisher() - let audioPlayerStates = Array(repeating: AudioPlayerState(id: .timelineItemIdentifier(.random), title: "", duration: 0), count: 10) + let audioPlayerStates = Array(repeating: AudioPlayerState(id: .timelineItemIdentifier(.randomEvent), title: "", duration: 0), count: 10) for audioPlayerState in audioPlayerStates { mediaPlayerProvider.register(audioPlayerState: audioPlayerState) audioPlayerState.attachAudioPlayer(audioPlayer) diff --git a/UnitTests/Sources/MessageForwardingScreenViewModelTests.swift b/UnitTests/Sources/MessageForwardingScreenViewModelTests.swift index b0c8e2c96d..e96a8e2402 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: .init(uniqueID: "t1", eventOrTransactionID: .eventId(eventId: "t1")), + let forwardingItem = MessageForwardingItem(id: .event(uniqueID: "t1", eventOrTransactionID: .eventId(eventId: "t1")), roomID: "1", content: .init(noPointer: .init())) var viewModel: MessageForwardingScreenViewModelProtocol! diff --git a/UnitTests/Sources/ResolveVerifiedUserSendFailureScreenViewModelTests.swift b/UnitTests/Sources/ResolveVerifiedUserSendFailureScreenViewModelTests.swift index 91fc8e4279..f690d00367 100644 --- a/UnitTests/Sources/ResolveVerifiedUserSendFailureScreenViewModelTests.swift +++ b/UnitTests/Sources/ResolveVerifiedUserSendFailureScreenViewModelTests.swift @@ -61,7 +61,7 @@ class ResolveVerifiedUserSendFailureScreenViewModelTests: XCTestCase { private func makeViewModel(with failure: TimelineItemSendFailure.VerifiedUser) -> ResolveVerifiedUserSendFailureScreenViewModel { ResolveVerifiedUserSendFailureScreenViewModel(failure: failure, - itemID: .random, + itemID: .randomEvent, roomProxy: roomProxy, userIndicatorController: UserIndicatorControllerMock()) } diff --git a/UnitTests/Sources/TextBasedRoomTimelineTests.swift b/UnitTests/Sources/TextBasedRoomTimelineTests.swift index 775621fcbc..66a6d0e337 100644 --- a/UnitTests/Sources/TextBasedRoomTimelineTests.swift +++ b/UnitTests/Sources/TextBasedRoomTimelineTests.swift @@ -11,7 +11,7 @@ import XCTest final class TextBasedRoomTimelineTests: XCTestCase { func testTextRoomTimelineItemWhitespaceEnd() { let timestamp = "Now" - let timelineItem = TextRoomTimelineItem(id: .random, + let timelineItem = TextRoomTimelineItem(id: .randomEvent, timestamp: timestamp, isOutgoing: true, isEditable: true, @@ -24,7 +24,7 @@ final class TextBasedRoomTimelineTests: XCTestCase { func testTextRoomTimelineItemWhitespaceEndLonger() { let timestamp = "10:00 AM" - let timelineItem = TextRoomTimelineItem(id: .random, + let timelineItem = TextRoomTimelineItem(id: .randomEvent, timestamp: timestamp, isOutgoing: true, isEditable: true, @@ -37,7 +37,7 @@ final class TextBasedRoomTimelineTests: XCTestCase { func testTextRoomTimelineItemWhitespaceEndWithEdit() { let timestamp = "Now" - var timelineItem = TextRoomTimelineItem(id: .random, + var timelineItem = TextRoomTimelineItem(id: .randomEvent, timestamp: timestamp, isOutgoing: true, isEditable: true, @@ -52,7 +52,7 @@ final class TextBasedRoomTimelineTests: XCTestCase { func testTextRoomTimelineItemWhitespaceEndWithEditAndAlert() { let timestamp = "Now" - var timelineItem = TextRoomTimelineItem(id: .random, + var timelineItem = TextRoomTimelineItem(id: .randomEvent, timestamp: timestamp, isOutgoing: true, isEditable: true, diff --git a/UnitTests/Sources/TimelineViewModelTests.swift b/UnitTests/Sources/TimelineViewModelTests.swift index d1dcfc69b3..6625562c3f 100644 --- a/UnitTests/Sources/TimelineViewModelTests.swift +++ b/UnitTests/Sources/TimelineViewModelTests.swift @@ -423,7 +423,7 @@ class TimelineViewModelTests: XCTestCase { private extension TextRoomTimelineItem { init(text: String, sender: String, addReactions: Bool = false, addReadReceipts: [ReadReceipt] = []) { let reactions = addReactions ? [AggregatedReaction(accountOwnerID: "bob", key: "🦄", senders: [ReactionSender(id: sender, timestamp: Date())])] : [] - self.init(id: .random, + self.init(id: .randomEvent, timestamp: "10:47 am", isOutgoing: sender == "bob", isEditable: sender == "bob", @@ -437,13 +437,13 @@ private extension TextRoomTimelineItem { private extension SeparatorRoomTimelineItem { init(uniqueID: String) { - self.init(id: .init(uniqueID: uniqueID), text: "") + self.init(id: .virtual(uniqueID: uniqueID), text: "") } } private extension TextRoomTimelineItem { init(eventID: String) { - self.init(id: .init(uniqueID: UUID().uuidString, eventOrTransactionID: .eventId(eventId: eventID)), + self.init(id: .event(uniqueID: UUID().uuidString, eventOrTransactionID: .eventId(eventId: eventID)), timestamp: "", isOutgoing: false, isEditable: false,