From a7ff3aae0cac951bd46c73962e9e97b85a7b4b28 Mon Sep 17 00:00:00 2001 From: Jullian Mercier <31648126+jullianm@users.noreply.github.com> Date: Mon, 28 Oct 2024 17:37:19 +0100 Subject: [PATCH 1/9] process conversation message timer update event, add UTs - WPB-10171 --- .../ConversationMessageTimerUpdateEvent.swift | 2 +- .../project.pbxproj | 4 + ...tionMessageTimerUpdateEventProcessor.swift | 50 +++++++- .../ConversationLocalStore.swift | 40 +++++++ .../ConversationRepository.swift | 1 + .../generated/AutoMockable.generated.swift | 33 +++++ ...essageTimerUpdateEventProcessorTests.swift | 113 ++++++++++++++++++ 7 files changed, 238 insertions(+), 5 deletions(-) create mode 100644 WireDomain/Tests/WireDomainTests/Event Processing/ConversationEventProcessor/ConversationMessageTimerUpdateEventProcessorTests.swift diff --git a/WireAPI/Sources/WireAPI/Models/UpdateEvent/ConversationEvent/ConversationMessageTimerUpdateEvent.swift b/WireAPI/Sources/WireAPI/Models/UpdateEvent/ConversationEvent/ConversationMessageTimerUpdateEvent.swift index 3dae5c6e6cc..20e87d8b54d 100644 --- a/WireAPI/Sources/WireAPI/Models/UpdateEvent/ConversationEvent/ConversationMessageTimerUpdateEvent.swift +++ b/WireAPI/Sources/WireAPI/Models/UpdateEvent/ConversationEvent/ConversationMessageTimerUpdateEvent.swift @@ -20,7 +20,7 @@ import Foundation /// An event where the message timer of a conversation was updated. -public struct ConversationMessageTimerUpdateEvent: Equatable, Codable { +public struct ConversationMessageTimerUpdateEvent: Equatable, Codable, Sendable { /// The id of the conversation. diff --git a/WireDomain/Project/WireDomain Project.xcodeproj/project.pbxproj b/WireDomain/Project/WireDomain Project.xcodeproj/project.pbxproj index a7eaa958cde..e40cfef6f0c 100644 --- a/WireDomain/Project/WireDomain Project.xcodeproj/project.pbxproj +++ b/WireDomain/Project/WireDomain Project.xcodeproj/project.pbxproj @@ -65,6 +65,7 @@ C99322E72C986E3A0065E10F /* ConnectionsRepositoryError.swift in Sources */ = {isa = PBXBuildFile; fileRef = C99322CD2C986E3A0065E10F /* ConnectionsRepositoryError.swift */; }; C99322E82C986E3A0065E10F /* ConversationLabelsRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = C99322CF2C986E3A0065E10F /* ConversationLabelsRepository.swift */; }; C99322E92C986E3A0065E10F /* ConversationLabelsRepositoryError.swift in Sources */ = {isa = PBXBuildFile; fileRef = C99322D02C986E3A0065E10F /* ConversationLabelsRepositoryError.swift */; }; + C9C758542CCFF089001D45D7 /* ConversationMessageTimerUpdateEventProcessorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9C758532CCFF089001D45D7 /* ConversationMessageTimerUpdateEventProcessorTests.swift */; }; C9C8FDCE2C9DBE0E00702B91 /* FeatureConfigUpdateEventProcessorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9C8FDC32C9DBE0E00702B91 /* FeatureConfigUpdateEventProcessorTests.swift */; }; C9C8FDCF2C9DBE0E00702B91 /* TeamDeleteEventProcessorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9C8FDC52C9DBE0E00702B91 /* TeamDeleteEventProcessorTests.swift */; }; C9C8FDD02C9DBE0E00702B91 /* TeamMemberLeaveEventProcessorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9C8FDC62C9DBE0E00702B91 /* TeamMemberLeaveEventProcessorTests.swift */; }; @@ -204,6 +205,7 @@ C99322CD2C986E3A0065E10F /* ConnectionsRepositoryError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConnectionsRepositoryError.swift; sourceTree = ""; }; C99322CF2C986E3A0065E10F /* ConversationLabelsRepository.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConversationLabelsRepository.swift; sourceTree = ""; }; C99322D02C986E3A0065E10F /* ConversationLabelsRepositoryError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConversationLabelsRepositoryError.swift; sourceTree = ""; }; + C9C758532CCFF089001D45D7 /* ConversationMessageTimerUpdateEventProcessorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversationMessageTimerUpdateEventProcessorTests.swift; sourceTree = ""; }; C9C8FDC32C9DBE0E00702B91 /* FeatureConfigUpdateEventProcessorTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FeatureConfigUpdateEventProcessorTests.swift; sourceTree = ""; }; C9C8FDC52C9DBE0E00702B91 /* TeamDeleteEventProcessorTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TeamDeleteEventProcessorTests.swift; sourceTree = ""; }; C9C8FDC62C9DBE0E00702B91 /* TeamMemberLeaveEventProcessorTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TeamMemberLeaveEventProcessorTests.swift; sourceTree = ""; }; @@ -465,6 +467,7 @@ C98433FA2CCFD9A2009723D4 /* ConversationReceiptModeUpdateEventProcessorTests.swift */, C97C01B72CBD6D3C000683C5 /* ConversationCreateEventProcessorTests.swift */, C98433EB2CCA97AC009723D4 /* ConversationMemberLeaveEventTests.swift */, + C9C758532CCFF089001D45D7 /* ConversationMessageTimerUpdateEventProcessorTests.swift */, ); path = ConversationEventProcessor; sourceTree = ""; @@ -1029,6 +1032,7 @@ C9F691292C9B164A008CC41F /* UserPushRemoveEventProcessorTests.swift in Sources */, C93961932C91B15B00EA971A /* ConversationRepositoryTests.swift in Sources */, C93961922C91B12800EA971A /* TestError.swift in Sources */, + C9C758542CCFF089001D45D7 /* ConversationMessageTimerUpdateEventProcessorTests.swift in Sources */, EEC410262C60D48900E89394 /* SyncManagerTests.swift in Sources */, C9C8FDD32C9DBE0E00702B91 /* UserLegalHoldDisableEventProcessorTests.swift in Sources */, C98433F82CCFCFE5009723D4 /* ConversationProtocolUpdateEventProcessorTests.swift in Sources */, diff --git a/WireDomain/Sources/WireDomain/Event Processing/ConversationEventProcessor/ConversationMessageTimerUpdateEventProcessor.swift b/WireDomain/Sources/WireDomain/Event Processing/ConversationEventProcessor/ConversationMessageTimerUpdateEventProcessor.swift index f121db3fad4..192361835ae 100644 --- a/WireDomain/Sources/WireDomain/Event Processing/ConversationEventProcessor/ConversationMessageTimerUpdateEventProcessor.swift +++ b/WireDomain/Sources/WireDomain/Event Processing/ConversationEventProcessor/ConversationMessageTimerUpdateEventProcessor.swift @@ -17,6 +17,7 @@ // import WireAPI +import WireDataModel /// Process conversation message timer update events. @@ -26,15 +27,56 @@ protocol ConversationMessageTimerUpdateEventProcessorProtocol { /// /// - Parameter event: A conversation message timer update event. - func processEvent(_ event: ConversationMessageTimerUpdateEvent) async throws + func processEvent(_ event: ConversationMessageTimerUpdateEvent) async } struct ConversationMessageTimerUpdateEventProcessor: ConversationMessageTimerUpdateEventProcessorProtocol { - func processEvent(_: ConversationMessageTimerUpdateEvent) async throws { - // TODO: [WPB-10171] - assertionFailure("not implemented yet") + let userRepository: any UserRepositoryProtocol + let conversationRepository: any ConversationRepositoryProtocol + let conversationLocalStore: any ConversationLocalStoreProtocol + + func processEvent(_ event: ConversationMessageTimerUpdateEvent) async { + let userID = event.senderID + let conversationID = event.conversationID + let timer = Double(event.newTimer ?? 0) + let timestamp = event.timestamp + + let sender = await userRepository.fetchOrCreateUser( + with: userID.uuid, + domain: userID.domain + ) + + let conversation = await conversationRepository.fetchOrCreateConversation( + with: conversationID.uuid, + domain: conversationID.domain + ) + + let timeoutValue = timer / 1_000 + let timeout: MessageDestructionTimeoutValue = .init(rawValue: timeoutValue) + let currentTimeout = await conversationLocalStore.conversationMessageDestructionTimeout(conversation) + + if currentTimeout != timeout { + let systemMessage = SystemMessage( + type: .messageTimerUpdate, + sender: sender, + users: [sender], + timestamp: timestamp, + messageTimer: timeoutValue + ) + + // TODO: [WPB-11839] Use MessageRepository + await conversationRepository.addSystemMessage( + systemMessage, + to: conversation + ) + } + + await conversationLocalStore.storeConversation( + timeoutValue: timeoutValue, + for: conversation + ) } } diff --git a/WireDomain/Sources/WireDomain/Repositories/Conversations/ConversationLocalStore.swift b/WireDomain/Sources/WireDomain/Repositories/Conversations/ConversationLocalStore.swift index 9b033848773..421fc93aa65 100644 --- a/WireDomain/Sources/WireDomain/Repositories/Conversations/ConversationLocalStore.swift +++ b/WireDomain/Sources/WireDomain/Repositories/Conversations/ConversationLocalStore.swift @@ -184,6 +184,25 @@ public protocol ConversationLocalStoreProtocol { users: Set, initiatingUser: ZMUser ) async + + /// The conversation active message destruction timeout value. + /// - Parameters: + /// - conversation: The conversation to get the message destruction timeout value from. + /// - returns: A `MessageDestructionTimeoutValue` object. + + func conversationMessageDestructionTimeout( + _ conversation: ZMConversation + ) async -> MessageDestructionTimeoutValue + + /// Stores a message destruction timeout value. + /// - parameters: + /// - timeoutValue: The message destruction timeout value. + /// - conversation: The conversation to update the value for. + + func storeConversation( + timeoutValue: Double, + for conversation: ZMConversation + ) async } public final class ConversationLocalStore: ConversationLocalStoreProtocol { @@ -238,6 +257,7 @@ public final class ConversationLocalStore: ConversationLocalStoreProtocol { } } + // TODO: [WPB-11839] To be removed when MessageRepository PR is merged public func addSystemMessage( _ message: SystemMessage, to conversation: ZMConversation @@ -389,6 +409,26 @@ public final class ConversationLocalStore: ConversationLocalStoreProtocol { } } + public func conversationMessageDestructionTimeout( + _ conversation: ZMConversation + ) async -> MessageDestructionTimeoutValue { + await context.perform { + conversation.activeMessageDestructionTimeoutValue ?? .init(rawValue: 0) + } + } + + public func storeConversation( + timeoutValue: Double, + for conversation: ZMConversation + ) async { + await context.perform { + conversation.setMessageDestructionTimeoutValue( + .init(rawValue: timeoutValue), + for: .groupConversation + ) + } + } + public func storeConversation( isArchived: Bool, for conversation: ZMConversation diff --git a/WireDomain/Sources/WireDomain/Repositories/Conversations/ConversationRepository.swift b/WireDomain/Sources/WireDomain/Repositories/Conversations/ConversationRepository.swift index c36ce3b6889..44ae8f98bf2 100644 --- a/WireDomain/Sources/WireDomain/Repositories/Conversations/ConversationRepository.swift +++ b/WireDomain/Sources/WireDomain/Repositories/Conversations/ConversationRepository.swift @@ -365,6 +365,7 @@ public final class ConversationRepository: ConversationRepositoryProtocol { await deleteMembership(for: removedUserIDs, time: time) } + // TODO: [WPB-11839] To be removed when MessageRepository PR is merged public func addSystemMessage( _ message: SystemMessage, to conversation: ZMConversation diff --git a/WireDomain/Sources/WireDomainSupport/Sourcery/generated/AutoMockable.generated.swift b/WireDomain/Sources/WireDomainSupport/Sourcery/generated/AutoMockable.generated.swift index 222711b8ca9..ed48a7e33d4 100644 --- a/WireDomain/Sources/WireDomainSupport/Sourcery/generated/AutoMockable.generated.swift +++ b/WireDomain/Sources/WireDomainSupport/Sourcery/generated/AutoMockable.generated.swift @@ -425,6 +425,39 @@ public class MockConversationLocalStoreProtocol: ConversationLocalStoreProtocol await mock(conversation, users, initiatingUser) } + // MARK: - conversationMessageDestructionTimeout + + public var conversationMessageDestructionTimeout_Invocations: [ZMConversation] = [] + public var conversationMessageDestructionTimeout_MockMethod: ((ZMConversation) async -> MessageDestructionTimeoutValue)? + public var conversationMessageDestructionTimeout_MockValue: MessageDestructionTimeoutValue? + + public func conversationMessageDestructionTimeout(_ conversation: ZMConversation) async -> MessageDestructionTimeoutValue { + conversationMessageDestructionTimeout_Invocations.append(conversation) + + if let mock = conversationMessageDestructionTimeout_MockMethod { + return await mock(conversation) + } else if let mock = conversationMessageDestructionTimeout_MockValue { + return mock + } else { + fatalError("no mock for `conversationMessageDestructionTimeout`") + } + } + + // MARK: - storeConversation + + public var storeConversationTimeoutValueFor_Invocations: [(timeoutValue: Double, conversation: ZMConversation)] = [] + public var storeConversationTimeoutValueFor_MockMethod: ((Double, ZMConversation) async -> Void)? + + public func storeConversation(timeoutValue: Double, for conversation: ZMConversation) async { + storeConversationTimeoutValueFor_Invocations.append((timeoutValue: timeoutValue, conversation: conversation)) + + guard let mock = storeConversationTimeoutValueFor_MockMethod else { + fatalError("no mock for `storeConversationTimeoutValueFor`") + } + + await mock(timeoutValue, conversation) + } + } public class MockConversationRepositoryProtocol: ConversationRepositoryProtocol { diff --git a/WireDomain/Tests/WireDomainTests/Event Processing/ConversationEventProcessor/ConversationMessageTimerUpdateEventProcessorTests.swift b/WireDomain/Tests/WireDomainTests/Event Processing/ConversationEventProcessor/ConversationMessageTimerUpdateEventProcessorTests.swift new file mode 100644 index 00000000000..71fa987d829 --- /dev/null +++ b/WireDomain/Tests/WireDomainTests/Event Processing/ConversationEventProcessor/ConversationMessageTimerUpdateEventProcessorTests.swift @@ -0,0 +1,113 @@ +// +// Wire +// Copyright (C) 2024 Wire Swiss GmbH +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see http://www.gnu.org/licenses/. +// + +@testable import WireAPI +import WireDataModel +import WireDataModelSupport +@testable import WireDomain +import WireDomainSupport +import XCTest + +final class ConversationMessageTimerUpdateEventProcessorTests: XCTestCase { + + private var sut: ConversationMessageTimerUpdateEventProcessor! + private var userRepository: MockUserRepositoryProtocol! + private var conversationRepository: MockConversationRepositoryProtocol! + private var conversationLocalStore: MockConversationLocalStoreProtocol! + private var coreDataStack: CoreDataStack! + private var coreDataStackHelper: CoreDataStackHelper! + private var modelHelper: ModelHelper! + + private var context: NSManagedObjectContext { + coreDataStack.syncContext + } + + override func setUp() async throws { + try await super.setUp() + + modelHelper = ModelHelper() + coreDataStackHelper = CoreDataStackHelper() + coreDataStack = try await coreDataStackHelper.createStack() + + userRepository = MockUserRepositoryProtocol() + conversationRepository = MockConversationRepositoryProtocol() + conversationLocalStore = MockConversationLocalStoreProtocol() + + sut = ConversationMessageTimerUpdateEventProcessor( + userRepository: userRepository, + conversationRepository: conversationRepository, + conversationLocalStore: conversationLocalStore + ) + } + + override func tearDown() async throws { + try await super.tearDown() + sut = nil + userRepository = nil + conversationRepository = nil + conversationLocalStore = nil + modelHelper = nil + coreDataStack = nil + try coreDataStackHelper.cleanupDirectory() + coreDataStackHelper = nil + } + + // MARK: - Tests + + func testProcessEvent_It_Invokes_Repo_Methods() async { + // Mock + + let (user, conversation) = await context.perform { [self] in + let user = modelHelper.createUser(in: context) + let conversation = modelHelper.createGroupConversation(in: context) + + return (user, conversation) + } + + userRepository.fetchOrCreateUserWithDomain_MockValue = user + conversationRepository.fetchOrCreateConversationWithDomain_MockValue = conversation + conversationRepository.addSystemMessageTo_MockMethod = { _, _ in } + conversationLocalStore.conversationMessageDestructionTimeout_MockValue = .fiveMinutes + conversationLocalStore.storeConversationTimeoutValueFor_MockMethod = { _, _ in } + + // When + + await sut.processEvent(Scaffolding.event) + + // Then + + XCTAssertEqual(userRepository.fetchOrCreateUserWithDomain_Invocations.count, 1) + XCTAssertEqual(conversationRepository.fetchOrCreateConversationWithDomain_Invocations.count, 1) + XCTAssertEqual(conversationRepository.addSystemMessageTo_Invocations.count, 1) + XCTAssertEqual(conversationLocalStore.conversationMessageDestructionTimeout_Invocations.count, 1) + XCTAssertEqual(conversationLocalStore.storeConversationTimeoutValueFor_Invocations.count, 1) + } + + private enum Scaffolding { + static let id = UUID() + static let domain = "domain.com" + static let event = ConversationMessageTimerUpdateEvent( + conversationID: ConversationID(uuid: id, domain: domain), + senderID: UserID(uuid: id, domain: domain), + timestamp: .now, + newTimer: 10_000 + ) + + } + +} From 94b4ad4ad0103ea30b3f2355baaee2a074dd5797 Mon Sep 17 00:00:00 2001 From: Jullian Mercier <31648126+jullianm@users.noreply.github.com> Date: Tue, 29 Oct 2024 14:24:55 +0100 Subject: [PATCH 2/9] create Conversation domain model object, refactor local store/repo, rename parameters - WPB-10171 --- .../project.pbxproj | 4 + .../ConversationCreateEventProcessor.swift | 2 +- .../ConversationLocalStore+Group.swift | 22 ++- .../ConversationLocalStore+MLS.swift | 32 ++-- .../ConversationLocalStore+Metadata.swift | 24 +-- .../ConversationLocalStore+Status.swift | 46 +++-- .../ConversationLocalStore.swift | 163 ++++++++++-------- .../ConversationModelMappings.swift | 77 +++++++++ .../ConversationRepository.swift | 16 +- .../Conversations/Models/Conversation.swift | 63 +++++++ .../generated/AutoMockable.generated.swift | 40 ++--- .../ConversationRepositoryTests.swift | 2 +- 12 files changed, 342 insertions(+), 149 deletions(-) create mode 100644 WireDomain/Sources/WireDomain/Repositories/Conversations/Models/Conversation.swift diff --git a/WireDomain/Project/WireDomain Project.xcodeproj/project.pbxproj b/WireDomain/Project/WireDomain Project.xcodeproj/project.pbxproj index e40cfef6f0c..a1170083ace 100644 --- a/WireDomain/Project/WireDomain Project.xcodeproj/project.pbxproj +++ b/WireDomain/Project/WireDomain Project.xcodeproj/project.pbxproj @@ -66,6 +66,7 @@ C99322E82C986E3A0065E10F /* ConversationLabelsRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = C99322CF2C986E3A0065E10F /* ConversationLabelsRepository.swift */; }; C99322E92C986E3A0065E10F /* ConversationLabelsRepositoryError.swift in Sources */ = {isa = PBXBuildFile; fileRef = C99322D02C986E3A0065E10F /* ConversationLabelsRepositoryError.swift */; }; C9C758542CCFF089001D45D7 /* ConversationMessageTimerUpdateEventProcessorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9C758532CCFF089001D45D7 /* ConversationMessageTimerUpdateEventProcessorTests.swift */; }; + C9C758582CD0DD17001D45D7 /* Conversation.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9C758572CD0DD17001D45D7 /* Conversation.swift */; }; C9C8FDCE2C9DBE0E00702B91 /* FeatureConfigUpdateEventProcessorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9C8FDC32C9DBE0E00702B91 /* FeatureConfigUpdateEventProcessorTests.swift */; }; C9C8FDCF2C9DBE0E00702B91 /* TeamDeleteEventProcessorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9C8FDC52C9DBE0E00702B91 /* TeamDeleteEventProcessorTests.swift */; }; C9C8FDD02C9DBE0E00702B91 /* TeamMemberLeaveEventProcessorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9C8FDC62C9DBE0E00702B91 /* TeamMemberLeaveEventProcessorTests.swift */; }; @@ -206,6 +207,7 @@ C99322CF2C986E3A0065E10F /* ConversationLabelsRepository.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConversationLabelsRepository.swift; sourceTree = ""; }; C99322D02C986E3A0065E10F /* ConversationLabelsRepositoryError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConversationLabelsRepositoryError.swift; sourceTree = ""; }; C9C758532CCFF089001D45D7 /* ConversationMessageTimerUpdateEventProcessorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversationMessageTimerUpdateEventProcessorTests.swift; sourceTree = ""; }; + C9C758572CD0DD17001D45D7 /* Conversation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Conversation.swift; sourceTree = ""; }; C9C8FDC32C9DBE0E00702B91 /* FeatureConfigUpdateEventProcessorTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FeatureConfigUpdateEventProcessorTests.swift; sourceTree = ""; }; C9C8FDC52C9DBE0E00702B91 /* TeamDeleteEventProcessorTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TeamDeleteEventProcessorTests.swift; sourceTree = ""; }; C9C8FDC62C9DBE0E00702B91 /* TeamMemberLeaveEventProcessorTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TeamMemberLeaveEventProcessorTests.swift; sourceTree = ""; }; @@ -476,6 +478,7 @@ isa = PBXGroup; children = ( C97C01D42CC138EC000683C5 /* SystemMessage.swift */, + C9C758572CD0DD17001D45D7 /* Conversation.swift */, ); path = Models; sourceTree = ""; @@ -1011,6 +1014,7 @@ C99322D82C986E3A0065E10F /* UpdateEventsRepository.swift in Sources */, EE368CD12C2DAA87009DBAB0 /* TeamEventProcessor.swift in Sources */, C99322D32C986E3A0065E10F /* TeamRepositoryError.swift in Sources */, + C9C758582CD0DD17001D45D7 /* Conversation.swift in Sources */, EEAD0A222C46ABFB00CC8658 /* UserLegalholdDisableEventProcessor.swift in Sources */, EEAD09F22C46604400CC8658 /* ConversationAccessUpdateEventProcessor.swift in Sources */, EEAD0A082C46776400CC8658 /* ConversationProtocolUpdateEventProcessor.swift in Sources */, diff --git a/WireDomain/Sources/WireDomain/Event Processing/ConversationEventProcessor/ConversationCreateEventProcessor.swift b/WireDomain/Sources/WireDomain/Event Processing/ConversationEventProcessor/ConversationCreateEventProcessor.swift index 34c6c3c5c6b..325d2b0b853 100644 --- a/WireDomain/Sources/WireDomain/Event Processing/ConversationEventProcessor/ConversationCreateEventProcessor.swift +++ b/WireDomain/Sources/WireDomain/Event Processing/ConversationEventProcessor/ConversationCreateEventProcessor.swift @@ -51,7 +51,7 @@ struct ConversationCreateEventProcessor: ConversationCreateEventProcessorProtoco } await repository.storeConversation( - conversation, + conversation.toDomainModel(), timestamp: timestamp ) } diff --git a/WireDomain/Sources/WireDomain/Repositories/Conversations/ConversationLocalStore+Group.swift b/WireDomain/Sources/WireDomain/Repositories/Conversations/ConversationLocalStore+Group.swift index 0e5f7b18852..6a5f44556c7 100644 --- a/WireDomain/Sources/WireDomain/Repositories/Conversations/ConversationLocalStore+Group.swift +++ b/WireDomain/Sources/WireDomain/Repositories/Conversations/ConversationLocalStore+Group.swift @@ -26,16 +26,16 @@ extension ConversationLocalStore { // MARK: - User & Role func fetchUserAndRole( - from remoteConversationMember: WireAPI.Conversation.Member, + from conversationMember: Conversation.Members.Member, for localConversation: ZMConversation ) -> (user: ZMUser, role: Role?)? { - guard let userID = remoteConversationMember.id ?? remoteConversationMember.qualifiedID?.uuid else { + guard let userID = conversationMember.id ?? conversationMember.qualifiedID?.uuid else { return nil } let user = ZMUser.fetchOrCreate( with: userID, - domain: remoteConversationMember.qualifiedID?.domain, + domain: conversationMember.qualifiedID?.domain, in: context ) @@ -50,8 +50,11 @@ extension ConversationLocalStore { ) } - let role = remoteConversationMember.conversationRole.map { - fetchOrCreateRoleForConversation(name: $0, conversation: localConversation) + let role = conversationMember.conversationRole.map { + fetchOrCreateRoleForConversation( + name: $0, + conversation: localConversation + ) } return (user, role) @@ -60,10 +63,10 @@ extension ConversationLocalStore { // MARK: - Members func updateMembers( - from remoteConversation: WireAPI.Conversation, + from conversation: Conversation, for localConversation: ZMConversation ) { - guard let members = remoteConversation.members else { + guard let members = conversation.members else { return } @@ -79,7 +82,10 @@ extension ConversationLocalStore { for: localConversation )?.role - localConversation.updateMembers(otherMembers, selfUserRole: selfUserRole) + localConversation.updateMembers( + otherMembers, + selfUserRole: selfUserRole + ) } // MARK: - 1:1 diff --git a/WireDomain/Sources/WireDomain/Repositories/Conversations/ConversationLocalStore+MLS.swift b/WireDomain/Sources/WireDomain/Repositories/Conversations/ConversationLocalStore+MLS.swift index 36a7c3aa5ee..0531f14c025 100644 --- a/WireDomain/Sources/WireDomain/Repositories/Conversations/ConversationLocalStore+MLS.swift +++ b/WireDomain/Sources/WireDomain/Repositories/Conversations/ConversationLocalStore+MLS.swift @@ -26,24 +26,24 @@ extension ConversationLocalStore { // MARK: - Message protocols func assignMessageProtocol( - from remoteConversation: WireAPI.Conversation, + from conversation: Conversation, for localConversation: ZMConversation ) { - guard let newMessageProtocol = remoteConversation.messageProtocol else { + guard let newMessageProtocol = conversation.messageProtocol else { eventProcessingLogger.warn( "message protocol is missing" ) return } - localConversation.messageProtocol = newMessageProtocol.toDomainModel() + localConversation.messageProtocol = newMessageProtocol } func updateMessageProtocol( - from remoteConversation: WireAPI.Conversation, + from conversation: Conversation, for localConversation: ZMConversation ) { - guard let newMessageProtocol = remoteConversation.messageProtocol else { + guard let newMessageProtocol = conversation.messageProtocol else { eventProcessingLogger.warn( "message protocol is missing" ) @@ -59,12 +59,16 @@ extension ConversationLocalStore { break /// no update, ignore case .mixed: localConversation.appendMLSMigrationStartedSystemMessage(sender: sender, at: .now) - localConversation.messageProtocol = newMessageProtocol.toDomainModel() + localConversation.messageProtocol = newMessageProtocol case .mls: let date = localConversation.lastModifiedDate ?? .now - localConversation.appendMLSMigrationPotentialGapSystemMessage(sender: sender, at: date) - localConversation.messageProtocol = newMessageProtocol.toDomainModel() + + localConversation.appendMLSMigrationPotentialGapSystemMessage( + sender: sender, at: date + ) + + localConversation.messageProtocol = newMessageProtocol } case .mixed: @@ -78,7 +82,7 @@ extension ConversationLocalStore { break /// no update, ignore case .mls: localConversation.appendMLSMigrationFinalizedSystemMessage(sender: sender, at: .now) - localConversation.messageProtocol = newMessageProtocol.toDomainModel() + localConversation.messageProtocol = newMessageProtocol } case .mls: @@ -119,8 +123,14 @@ extension ConversationLocalStore { ) if await context.perform({ localConversation.epoch <= 0 }) { - let ciphersuite = try await mlsService.createSelfGroup(for: groupID) - await context.perform { localConversation.ciphersuite = ciphersuite } + let ciphersuite = try await mlsService.createSelfGroup( + for: groupID + ) + + await context.perform { + localConversation.ciphersuite = ciphersuite + } + } else if try await !mlsService.conversationExists(groupID: groupID) { try await mlsService.joinGroup(with: groupID) } diff --git a/WireDomain/Sources/WireDomain/Repositories/Conversations/ConversationLocalStore+Metadata.swift b/WireDomain/Sources/WireDomain/Repositories/Conversations/ConversationLocalStore+Metadata.swift index f669ac6c9a1..99ea4375ad5 100644 --- a/WireDomain/Sources/WireDomain/Repositories/Conversations/ConversationLocalStore+Metadata.swift +++ b/WireDomain/Sources/WireDomain/Repositories/Conversations/ConversationLocalStore+Metadata.swift @@ -26,25 +26,25 @@ extension ConversationLocalStore { // MARK: - Metadata func updateMetadata( - from remoteConversation: WireAPI.Conversation, + from conversation: Conversation, for localConversation: ZMConversation ) { - if let teamID = remoteConversation.teamID { + if let teamID = conversation.teamID { localConversation.updateTeam(identifier: teamID) } - if let name = remoteConversation.name { + if let name = conversation.name { localConversation.userDefinedName = name } - guard let userID = remoteConversation.creator else { + guard let userID = conversation.creator else { return } /// We assume that the creator always belongs to the same domain as the conversation let creator = ZMUser.fetchOrCreate( with: userID, - domain: remoteConversation.qualifiedID?.domain, + domain: conversation.qualifiedID?.domain, in: context ) @@ -54,28 +54,28 @@ extension ConversationLocalStore { // MARK: - Attributes func updateAttributes( - from remoteConversation: WireAPI.Conversation, + from conversation: Conversation, for localConversation: ZMConversation, isFederationEnabled: Bool ) { - localConversation.domain = isFederationEnabled ? remoteConversation.qualifiedID?.domain : nil + localConversation.domain = isFederationEnabled ? conversation.qualifiedID?.domain : nil localConversation.needsToBeUpdatedFromBackend = false - if let epoch = remoteConversation.epoch { + if let epoch = conversation.epoch { localConversation.epoch = UInt64(epoch) } - let base64String = remoteConversation.mlsGroupID + let base64String = conversation.mlsGroupID if let base64String, let mlsGroupID = MLSGroupID(base64Encoded: base64String) { localConversation.mlsGroupID = mlsGroupID } - let ciphersuite = remoteConversation.cipherSuite - let epoch = remoteConversation.epoch + let ciphersuite = conversation.cipherSuite + let epoch = conversation.epoch if let ciphersuite, let epoch, epoch > 0 { - localConversation.ciphersuite = ciphersuite.toDomainModel() + localConversation.ciphersuite = ciphersuite } } diff --git a/WireDomain/Sources/WireDomain/Repositories/Conversations/ConversationLocalStore+Status.swift b/WireDomain/Sources/WireDomain/Repositories/Conversations/ConversationLocalStore+Status.swift index 847123e8580..c95775f0096 100644 --- a/WireDomain/Sources/WireDomain/Repositories/Conversations/ConversationLocalStore+Status.swift +++ b/WireDomain/Sources/WireDomain/Repositories/Conversations/ConversationLocalStore+Status.swift @@ -26,7 +26,7 @@ extension ConversationLocalStore { // MARK: - Conversation status func updateConversationStatus( - from remoteConversation: WireAPI.Conversation, + from remoteConversation: Conversation, for localConversation: ZMConversation ) { if let selfMember = remoteConversation.members?.selfMember { @@ -42,10 +42,18 @@ extension ConversationLocalStore { if let accessModes = remoteConversation.access { if let accessRoles = remoteConversation.accessRoles { - localConversation.updateAccessStatus(accessModes: accessModes.map(\.rawValue), accessRoles: accessRoles.map(\.rawValue)) + localConversation.updateAccessStatus( + accessModes: accessModes, + accessRoles: accessRoles + ) + } else if let accessRole = remoteConversation.legacyAccessRole { - let accessRoles = ConversationAccessRoleV2.fromLegacyAccessRole(accessRole.toDomainModel()) - localConversation.updateAccessStatus(accessModes: accessModes.map(\.rawValue), accessRoles: accessRoles.map(\.rawValue)) + let accessRoles = ConversationAccessRoleV2.fromLegacyAccessRole(accessRole) + + localConversation.updateAccessStatus( + accessModes: accessModes, + accessRoles: accessRoles.map(\.rawValue) + ) } } @@ -57,14 +65,16 @@ extension ConversationLocalStore { // MARK: - MLS status func updateMLSStatus( - from remoteConversation: WireAPI.Conversation, + from remoteConversation: Conversation, for localConversation: ZMConversation ) async { guard DeveloperFlag.enableMLSSupport.isOn else { return } await updateConversationIfNeeded( localConversation: localConversation, - fallbackGroupID: remoteConversation.mlsGroupID.map { .init(base64Encoded: $0) } ?? nil + fallbackGroupID: remoteConversation.mlsGroupID.map { + .init(base64Encoded: $0) + } ?? nil ) } @@ -99,7 +109,9 @@ extension ConversationLocalStore { let conversationExists: Bool do { - conversationExists = try await mlsService.conversationExists(groupID: mlsGroupID) + conversationExists = try await mlsService.conversationExists( + groupID: mlsGroupID + ) } catch { conversationExists = false } @@ -115,21 +127,27 @@ extension ConversationLocalStore { // MARK: - Member status func updateMemberStatus( - from remoteConversation: WireAPI.Conversation.Member, + from conversationMember: Conversation.Members.Member, for localConversation: ZMConversation ) { - let mutedStatus = remoteConversation.mutedStatus - let mutedReference = remoteConversation.mutedReference + let mutedStatus = conversationMember.mutedStatus + let mutedReference = conversationMember.mutedReference if let mutedStatus, let mutedReference { - localConversation.updateMutedStatus(status: Int32(mutedStatus), referenceDate: mutedReference) + localConversation.updateMutedStatus( + status: Int32(mutedStatus), + referenceDate: mutedReference + ) } - let archived = remoteConversation.archived - let archivedReference = remoteConversation.archivedReference + let archived = conversationMember.archived + let archivedReference = conversationMember.archivedReference if let archived, let archivedReference { - localConversation.updateArchivedStatus(archived: archived, referenceDate: archivedReference) + localConversation.updateArchivedStatus( + archived: archived, + referenceDate: archivedReference + ) } } diff --git a/WireDomain/Sources/WireDomain/Repositories/Conversations/ConversationLocalStore.swift b/WireDomain/Sources/WireDomain/Repositories/Conversations/ConversationLocalStore.swift index 421fc93aa65..65ef1e466b2 100644 --- a/WireDomain/Sources/WireDomain/Repositories/Conversations/ConversationLocalStore.swift +++ b/WireDomain/Sources/WireDomain/Repositories/Conversations/ConversationLocalStore.swift @@ -17,7 +17,6 @@ // import CoreData -import WireAPI import WireDataModel // sourcery: AutoMockable @@ -59,25 +58,29 @@ public protocol ConversationLocalStoreProtocol { /// - Parameter isFederationEnabled: A flag indicating whether a `Federation` is enabled. func storeConversation( - _ conversation: WireAPI.Conversation, + _ conversation: WireDomain.Conversation, timestamp: Date, isFederationEnabled: Bool ) async /// Stores a flag indicating whether a conversation requires an update from backend. /// - Parameter needsBackendUpdate: A flag indicated whether the qualified conversation needs to be updated from backend. - /// - Parameter qualifiedId: The conversation qualified ID. + /// - Parameter conversationID: The conversation ID. + /// - Parameter conversationDomain: The conversation domain. func storeConversation( needsBackendUpdate: Bool, - qualifiedId: WireAPI.QualifiedID + conversationID: UUID, + conversationDomain: String ) async /// Stores a given failed conversation locally. - /// - Parameter qualifiedId: The conversation qualified ID. + /// - Parameter conversationID: The conversation ID. + /// - Parameter conversationDomain: The conversation domain. func storeFailedConversation( - withQualifiedId qualifiedId: WireAPI.QualifiedID + conversationID: UUID, + conversationDomain: String ) async /// Fetches a MLS conversation locally. @@ -288,7 +291,7 @@ public final class ConversationLocalStore: ConversationLocalStoreProtocol { } public func storeConversation( - _ conversation: WireAPI.Conversation, + _ conversation: Conversation, timestamp: Date, isFederationEnabled: Bool ) async { @@ -317,16 +320,16 @@ public final class ConversationLocalStore: ConversationLocalStoreProtocol { switch conversationType { case .group: await updateOrCreateGroupConversation( - remoteConversation: conversation, - remoteConversationID: id, + from: conversation, + withID: id, serverTimestamp: timestamp, isFederationEnabled: isFederationEnabled ) case .`self`: await updateOrCreateSelfConversation( - remoteConversation: conversation, - remoteConversationID: id, + from: conversation, + withID: id, serverTimestamp: timestamp, isFederationEnabled: isFederationEnabled ) @@ -335,8 +338,8 @@ public final class ConversationLocalStore: ConversationLocalStoreProtocol { /// Conversations are of type `connection` while the connection /// is pending. await updateOrCreateConnectionConversation( - remoteConversation: conversation, - remoteConversationID: id, + from: conversation, + withID: id, serverTimestamp: timestamp, isFederationEnabled: isFederationEnabled ) @@ -345,8 +348,8 @@ public final class ConversationLocalStore: ConversationLocalStoreProtocol { /// Conversations are of type `oneOnOne` when the connection /// is accepted. await updateOrCreateOneToOneConversation( - remoteConversation: conversation, - remoteConversationID: id, + from: conversation, + withID: id, serverTimestamp: timestamp, isFederationEnabled: isFederationEnabled ) @@ -355,12 +358,13 @@ public final class ConversationLocalStore: ConversationLocalStoreProtocol { public func storeConversation( needsBackendUpdate: Bool, - qualifiedId: WireAPI.QualifiedID + conversationID: UUID, + conversationDomain: String ) async { await context.perform { [context] in let conversation = ZMConversation.fetch( - with: qualifiedId.uuid, - domain: qualifiedId.domain, + with: conversationID, + domain: conversationDomain, in: context ) @@ -369,11 +373,12 @@ public final class ConversationLocalStore: ConversationLocalStoreProtocol { } public func storeFailedConversation( - withQualifiedId qualifiedId: WireAPI.QualifiedID + conversationID: UUID, + conversationDomain: String ) async { await fetchOrCreateConversation( - conversationID: qualifiedId.uuid, - domain: qualifiedId.domain + conversationID: conversationID, + conversationDomain: conversationDomain ) { $0.isPendingMetadataRefresh = true $0.needsToBeUpdatedFromBackend = true @@ -543,31 +548,31 @@ public final class ConversationLocalStore: ConversationLocalStoreProtocol { /// /// See and for more information. /// - /// - Parameter remoteConversation: The conversation object received from backend. - /// - Parameter removeConversationID: The conversation ID received from backend. + /// - Parameter conversation: The up-to-date conversation object. + /// - Parameter id: The conversation ID. /// - Parameter isFederationEnabled: A flag indicating whether a federation is enabled. private func updateOrCreateConnectionConversation( - remoteConversation: WireAPI.Conversation, - remoteConversationID: UUID, + from conversation: Conversation, + withID id: UUID, serverTimestamp: Date, isFederationEnabled: Bool ) async { await fetchOrCreateConversation( - conversationID: remoteConversationID, - domain: remoteConversation.qualifiedID?.domain + conversationID: id, + conversationDomain: conversation.qualifiedID?.domain ) { [self] in $0.conversationType = .connection commonUpdate( - from: remoteConversation, + from: conversation, for: $0, serverTimestamp: serverTimestamp, isFederationEnabled: isFederationEnabled ) - assignMessageProtocol(from: remoteConversation, for: $0) - updateConversationStatus(from: remoteConversation, for: $0) + assignMessageProtocol(from: conversation, for: $0) + updateConversationStatus(from: conversation, for: $0) $0.needsToBeUpdatedFromBackend = false $0.isPendingInitialFetch = false @@ -580,32 +585,32 @@ public final class ConversationLocalStore: ConversationLocalStoreProtocol { /// /// See and for more information. /// - /// - Parameter remoteConversation: The conversation object received from backend. - /// - Parameter removeConversationID: The conversation ID received from backend. + /// - Parameter conversation: The up-to-date conversation object. + /// - Parameter id: The conversation ID. /// - Parameter isFederationEnabled: A flag indicating whether a federation is enabled. private func updateOrCreateSelfConversation( - remoteConversation: WireAPI.Conversation, - remoteConversationID: UUID, + from conversation: Conversation, + withID id: UUID, serverTimestamp: Date, isFederationEnabled: Bool ) async { - let (conversation, mlsGroupID) = await fetchOrCreateConversation( - conversationID: remoteConversationID, - domain: remoteConversation.qualifiedID?.domain + let (localConversation, mlsGroupID) = await fetchOrCreateConversation( + conversationID: id, + conversationDomain: conversation.qualifiedID?.domain ) { [self] in $0.conversationType = .`self` $0.isPendingMetadataRefresh = false commonUpdate( - from: remoteConversation, + from: conversation, for: $0, serverTimestamp: serverTimestamp, isFederationEnabled: isFederationEnabled ) - updateMessageProtocol(from: remoteConversation, for: $0) + updateMessageProtocol(from: conversation, for: $0) $0.isPendingInitialFetch = false $0.needsToBeUpdatedFromBackend = false @@ -615,7 +620,7 @@ public final class ConversationLocalStore: ConversationLocalStoreProtocol { if mlsGroupID != nil { do { - try await createOrJoinSelfConversation(from: conversation) + try await createOrJoinSelfConversation(from: localConversation) } catch { mlsLogger.error( "createOrJoinSelfConversation threw error: \(String(reflecting: error))" @@ -628,43 +633,43 @@ public final class ConversationLocalStore: ConversationLocalStoreProtocol { /// /// See and for more information. /// - /// - Parameter remoteConversation: The conversation object received from backend. - /// - Parameter removeConversationID: The conversation ID received from backend. + /// - Parameter conversation: The up-to-date conversation object. + /// - Parameter id: The conversation ID. /// - Parameter isFederationEnabled: A flag indicating whether a federation is enabled. private func updateOrCreateGroupConversation( - remoteConversation: WireAPI.Conversation, - remoteConversationID: UUID, + from conversation: Conversation, + withID id: UUID, serverTimestamp: Date, isFederationEnabled: Bool ) async { var isInitialFetch = false - let (conversation, _) = await fetchOrCreateConversation( - conversationID: remoteConversationID, - domain: remoteConversation.qualifiedID?.domain + let (localConversation, _) = await fetchOrCreateConversation( + conversationID: id, + conversationDomain: conversation.qualifiedID?.domain ) { [self] in isInitialFetch = $0.isPendingInitialFetch $0.conversationType = .group - $0.remoteIdentifier = remoteConversationID + $0.remoteIdentifier = id $0.isPendingMetadataRefresh = false $0.isPendingInitialFetch = false commonUpdate( - from: remoteConversation, + from: conversation, for: $0, serverTimestamp: serverTimestamp, isFederationEnabled: isFederationEnabled ) - updateConversationStatus(from: remoteConversation, for: $0) + updateConversationStatus(from: conversation, for: $0) if isInitialFetch { - assignMessageProtocol(from: remoteConversation, for: $0) + assignMessageProtocol(from: conversation, for: $0) } else { - updateMessageProtocol(from: remoteConversation, for: $0) + updateMessageProtocol(from: conversation, for: $0) } Flow.createGroup.checkpoint( @@ -674,18 +679,18 @@ public final class ConversationLocalStore: ConversationLocalStoreProtocol { return ($0, $0.mlsGroupID) } - await updateMLSStatus(from: remoteConversation, for: conversation) + await updateMLSStatus(from: conversation, for: localConversation) await context.perform { [self] in if isInitialFetch { /// we just got a new conversation, we display new conversation header - conversation.appendNewConversationSystemMessage( + localConversation.appendNewConversationSystemMessage( at: .distantPast, - users: conversation.localParticipants + users: localConversation.localParticipants ) /// Slow synced conversations should be considered read from the start - conversation.lastReadServerTimeStamp = conversation.lastModifiedDate + localConversation.lastReadServerTimeStamp = localConversation.lastModifiedDate Flow.createGroup.checkpoint( description: "new system message for conversation inserted" @@ -694,7 +699,7 @@ public final class ConversationLocalStore: ConversationLocalStoreProtocol { /// If we discover this group is actually a fake one on one, /// then we should link the one on one user. - linkOneOnOneUserIfNeeded(for: conversation) + linkOneOnOneUserIfNeeded(for: localConversation) } } @@ -702,23 +707,23 @@ public final class ConversationLocalStore: ConversationLocalStoreProtocol { /// /// See and for more information. /// - /// - Parameter remoteConversation: The conversation object received from backend. - /// - Parameter removeConversationID: The conversation ID received from backend. + /// - Parameter conversation: The up-to-date conversation object. + /// - Parameter id: The conversation ID. /// - Parameter isFederationEnabled: A flag indicating whether a federation is enabled. private func updateOrCreateOneToOneConversation( - remoteConversation: WireAPI.Conversation, - remoteConversationID: UUID, + from conversation: Conversation, + withID id: UUID, serverTimestamp: Date, isFederationEnabled: Bool ) async { - guard let conversationTypeRawValue = remoteConversation.type?.rawValue else { + guard let conversationTypeRawValue = conversation.type?.rawValue else { return } await fetchOrCreateConversation( - conversationID: remoteConversationID, - domain: remoteConversation.qualifiedID?.domain + conversationID: id, + conversationDomain: conversation.qualifiedID?.domain ) { [self] in let conversationType = BackendConversationType.clientConversationType( rawValue: conversationTypeRawValue @@ -730,14 +735,22 @@ public final class ConversationLocalStore: ConversationLocalStoreProtocol { $0.conversationType = conversationType } - assignMessageProtocol(from: remoteConversation, for: $0) + assignMessageProtocol( + from: conversation, + for: $0 + ) + commonUpdate( - from: remoteConversation, + from: conversation, for: $0, serverTimestamp: serverTimestamp, isFederationEnabled: isFederationEnabled ) - updateConversationStatus(from: remoteConversation, for: $0) + + updateConversationStatus( + from: conversation, for: $0 + ) + linkOneOnOneUserIfNeeded(for: $0) $0.needsToBeUpdatedFromBackend = false @@ -753,30 +766,30 @@ public final class ConversationLocalStore: ConversationLocalStoreProtocol { /// A common update method for all conversations received, no matter the type of the conversation. /// - /// - Parameter remoteConversation: The conversation object received from backend. + /// - Parameter conversation: The up-to-date conversation object. /// - Parameter localConversation: The local conversation to update. /// - Parameter isFederationEnabled: A flag indicating whether a federation is enabled. /// - Parameter serverTimestamp: The date the conversation was created/updated. private func commonUpdate( - from remoteConversation: WireAPI.Conversation, + from conversation: Conversation, for localConversation: ZMConversation, serverTimestamp: Date, isFederationEnabled: Bool ) { updateAttributes( - from: remoteConversation, + from: conversation, for: localConversation, isFederationEnabled: isFederationEnabled ) updateMetadata( - from: remoteConversation, + from: conversation, for: localConversation ) updateMembers( - from: remoteConversation, + from: conversation, for: localConversation ) @@ -789,7 +802,7 @@ public final class ConversationLocalStore: ConversationLocalStoreProtocol { /// A helper method (for all conversations) that fetches or creates a conversation locally and executes a completion block. /// /// - Parameter conversationID: The conversation ID to fetch or create the local conversation from. - /// - Parameter domain: The domain to fetch or create the conversation from. + /// - Parameter conversationDomain: The domain to fetch or create the conversation from. /// - Parameter handler: A completion block that takes a `ZMConversation` as argument and returns /// a `ZMConversation` and an optional `MLSGroupID`. /// @@ -799,13 +812,13 @@ public final class ConversationLocalStore: ConversationLocalStoreProtocol { @discardableResult private func fetchOrCreateConversation( conversationID: UUID, - domain: String?, + conversationDomain: String?, handler: @escaping (ZMConversation) -> (ZMConversation, MLSGroupID?) ) async -> (ZMConversation, MLSGroupID?) { await context.perform { [self] in let conversation = ZMConversation.fetchOrCreate( with: conversationID, - domain: domain, + domain: conversationDomain, in: context ) diff --git a/WireDomain/Sources/WireDomain/Repositories/Conversations/ConversationModelMappings.swift b/WireDomain/Sources/WireDomain/Repositories/Conversations/ConversationModelMappings.swift index 0fcf229f41b..8c2b62c7aa7 100644 --- a/WireDomain/Sources/WireDomain/Repositories/Conversations/ConversationModelMappings.swift +++ b/WireDomain/Sources/WireDomain/Repositories/Conversations/ConversationModelMappings.swift @@ -83,3 +83,80 @@ extension WireAPI.ConversationMemberLeaveReason { } } } + +extension WireAPI.ConversationType { + + func toDomainModel() -> WireDataModel.BackendConversationType { + switch self { + case .group: + .group + case .self: + .`self` + case .oneOnOne: + .oneOnOne + case .connection: + .connection + } + } + +} + +extension WireAPI.Conversation.Members { + + func toDomainModel() -> WireDomain.Conversation.Members { + .init( + others: others.map { $0.toDomainModel() }, + selfMember: selfMember.toDomainModel() + ) + } + +} + +extension WireAPI.Conversation.Member { + + func toDomainModel() -> WireDomain.Conversation.Members.Member { + .init( + qualifiedID: qualifiedID?.toDomainModel(), + id: id, + qualifiedTarget: qualifiedTarget?.toDomainModel(), + target: target, + conversationRole: conversationRole, + service: (service != nil) ? (service!.id, service!.provider) : nil, + archived: archived, + archivedReference: archivedReference, + hidden: hidden, + hiddenReference: hiddenReference, + mutedStatus: mutedStatus, + mutedReference: mutedReference + ) + } + +} + +extension WireAPI.Conversation { + + func toDomainModel() -> WireDomain.Conversation { + .init( + id: id, + qualifiedID: qualifiedID?.toDomainModel(), + teamID: teamID, + type: type?.toDomainModel(), + messageProtocol: messageProtocol?.toDomainModel(), + mlsGroupID: mlsGroupID, + cipherSuite: cipherSuite?.toDomainModel(), + epoch: epoch, + epochTimestamp: epochTimestamp, + creator: creator, + members: members?.toDomainModel(), + name: name, + messageTimer: messageTimer, + readReceiptMode: readReceiptMode, + access: access?.map(\.rawValue), + accessRoles: accessRoles?.map(\.rawValue), + legacyAccessRole: legacyAccessRole?.toDomainModel(), + lastEvent: lastEvent, + lastEventTime: lastEventTime + ) + } + +} diff --git a/WireDomain/Sources/WireDomain/Repositories/Conversations/ConversationRepository.swift b/WireDomain/Sources/WireDomain/Repositories/Conversations/ConversationRepository.swift index 44ae8f98bf2..b79e37c332b 100644 --- a/WireDomain/Sources/WireDomain/Repositories/Conversations/ConversationRepository.swift +++ b/WireDomain/Sources/WireDomain/Repositories/Conversations/ConversationRepository.swift @@ -47,7 +47,7 @@ public protocol ConversationRepositoryProtocol { /// - timestamp: The date the conversation was created or last modified. func storeConversation( - _ conversation: WireAPI.Conversation, + _ conversation: WireDomain.Conversation, timestamp: Date ) async @@ -173,7 +173,7 @@ public final class ConversationRepository: ConversationRepositoryProtocol { } await conversationsLocalStore.storeConversation( - conversation, + conversation.toDomainModel(), timestamp: .now, isFederationEnabled: backendInfo.isFederationEnabled ) @@ -200,7 +200,7 @@ public final class ConversationRepository: ConversationRepositoryProtocol { } public func storeConversation( - _ conversation: WireAPI.Conversation, + _ conversation: Conversation, timestamp: Date ) async { await conversationsLocalStore.storeConversation( @@ -236,7 +236,7 @@ public final class ConversationRepository: ConversationRepositoryProtocol { for conversation in foundConversations { taskGroup.addTask { [self] in await storeConversation( - conversation, + conversation.toDomainModel(), timestamp: .now ) } @@ -246,7 +246,8 @@ public final class ConversationRepository: ConversationRepositoryProtocol { taskGroup.addTask { [self] in await conversationsLocalStore.storeConversation( needsBackendUpdate: true, - qualifiedId: id + conversationID: id.uuid, + conversationDomain: id.domain ) } } @@ -254,7 +255,8 @@ public final class ConversationRepository: ConversationRepositoryProtocol { for id in failedConversationsQualifiedIds { taskGroup.addTask { [self] in await conversationsLocalStore.storeFailedConversation( - withQualifiedId: id + conversationID: id.uuid, + conversationDomain: id.domain ) } } @@ -275,7 +277,7 @@ public final class ConversationRepository: ConversationRepositoryProtocol { } await conversationsLocalStore.storeConversation( - mlsConversation, + mlsConversation.toDomainModel(), timestamp: .now, isFederationEnabled: backendInfo.isFederationEnabled ) diff --git a/WireDomain/Sources/WireDomain/Repositories/Conversations/Models/Conversation.swift b/WireDomain/Sources/WireDomain/Repositories/Conversations/Models/Conversation.swift new file mode 100644 index 00000000000..c14654eb77d --- /dev/null +++ b/WireDomain/Sources/WireDomain/Repositories/Conversations/Models/Conversation.swift @@ -0,0 +1,63 @@ +// +// Wire +// Copyright (C) 2024 Wire Swiss GmbH +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see http://www.gnu.org/licenses/. +// + +import WireDataModel + +public struct Conversation { + + struct Members { + let others: [Member] + let selfMember: Member + + struct Member { + let qualifiedID: QualifiedID? + let id: UUID? + let qualifiedTarget: QualifiedID? + let target: UUID? + let conversationRole: String? + let service: (id: UUID, provider: UUID)? + let archived: Bool? + let archivedReference: Date? + let hidden: Bool? + let hiddenReference: String? + let mutedStatus: Int? + let mutedReference: Date? + } + } + + let id: UUID? + let qualifiedID: QualifiedID? + let teamID: UUID? + let type: BackendConversationType? + let messageProtocol: MessageProtocol? + let mlsGroupID: String? + let cipherSuite: MLSCipherSuite? + let epoch: UInt? + let epochTimestamp: Date? + let creator: UUID? + let members: Members? + let name: String? + let messageTimer: TimeInterval? + let readReceiptMode: Int? + let access: [String]? + let accessRoles: [String]? + let legacyAccessRole: ConversationAccessRole? + let lastEvent: String? + let lastEventTime: Date? + +} diff --git a/WireDomain/Sources/WireDomainSupport/Sourcery/generated/AutoMockable.generated.swift b/WireDomain/Sources/WireDomainSupport/Sourcery/generated/AutoMockable.generated.swift index ed48a7e33d4..8b632124c3b 100644 --- a/WireDomain/Sources/WireDomainSupport/Sourcery/generated/AutoMockable.generated.swift +++ b/WireDomain/Sources/WireDomainSupport/Sourcery/generated/AutoMockable.generated.swift @@ -199,10 +199,10 @@ public class MockConversationLocalStoreProtocol: ConversationLocalStoreProtocol // MARK: - storeConversation - public var storeConversationTimestampIsFederationEnabled_Invocations: [(conversation: WireAPI.Conversation, timestamp: Date, isFederationEnabled: Bool)] = [] - public var storeConversationTimestampIsFederationEnabled_MockMethod: ((WireAPI.Conversation, Date, Bool) async -> Void)? + public var storeConversationTimestampIsFederationEnabled_Invocations: [(conversation: WireDomain.Conversation, timestamp: Date, isFederationEnabled: Bool)] = [] + public var storeConversationTimestampIsFederationEnabled_MockMethod: ((WireDomain.Conversation, Date, Bool) async -> Void)? - public func storeConversation(_ conversation: WireAPI.Conversation, timestamp: Date, isFederationEnabled: Bool) async { + public func storeConversation(_ conversation: WireDomain.Conversation, timestamp: Date, isFederationEnabled: Bool) async { storeConversationTimestampIsFederationEnabled_Invocations.append((conversation: conversation, timestamp: timestamp, isFederationEnabled: isFederationEnabled)) guard let mock = storeConversationTimestampIsFederationEnabled_MockMethod else { @@ -214,32 +214,32 @@ public class MockConversationLocalStoreProtocol: ConversationLocalStoreProtocol // MARK: - storeConversation - public var storeConversationNeedsBackendUpdateQualifiedId_Invocations: [(needsBackendUpdate: Bool, qualifiedId: WireAPI.QualifiedID)] = [] - public var storeConversationNeedsBackendUpdateQualifiedId_MockMethod: ((Bool, WireAPI.QualifiedID) async -> Void)? + public var storeConversationNeedsBackendUpdateConversationIDConversationDomain_Invocations: [(needsBackendUpdate: Bool, conversationID: UUID, conversationDomain: String)] = [] + public var storeConversationNeedsBackendUpdateConversationIDConversationDomain_MockMethod: ((Bool, UUID, String) async -> Void)? - public func storeConversation(needsBackendUpdate: Bool, qualifiedId: WireAPI.QualifiedID) async { - storeConversationNeedsBackendUpdateQualifiedId_Invocations.append((needsBackendUpdate: needsBackendUpdate, qualifiedId: qualifiedId)) + public func storeConversation(needsBackendUpdate: Bool, conversationID: UUID, conversationDomain: String) async { + storeConversationNeedsBackendUpdateConversationIDConversationDomain_Invocations.append((needsBackendUpdate: needsBackendUpdate, conversationID: conversationID, conversationDomain: conversationDomain)) - guard let mock = storeConversationNeedsBackendUpdateQualifiedId_MockMethod else { - fatalError("no mock for `storeConversationNeedsBackendUpdateQualifiedId`") + guard let mock = storeConversationNeedsBackendUpdateConversationIDConversationDomain_MockMethod else { + fatalError("no mock for `storeConversationNeedsBackendUpdateConversationIDConversationDomain`") } - await mock(needsBackendUpdate, qualifiedId) + await mock(needsBackendUpdate, conversationID, conversationDomain) } // MARK: - storeFailedConversation - public var storeFailedConversationWithQualifiedId_Invocations: [WireAPI.QualifiedID] = [] - public var storeFailedConversationWithQualifiedId_MockMethod: ((WireAPI.QualifiedID) async -> Void)? + public var storeFailedConversationConversationIDConversationDomain_Invocations: [(conversationID: UUID, conversationDomain: String)] = [] + public var storeFailedConversationConversationIDConversationDomain_MockMethod: ((UUID, String) async -> Void)? - public func storeFailedConversation(withQualifiedId qualifiedId: WireAPI.QualifiedID) async { - storeFailedConversationWithQualifiedId_Invocations.append(qualifiedId) + public func storeFailedConversation(conversationID: UUID, conversationDomain: String) async { + storeFailedConversationConversationIDConversationDomain_Invocations.append((conversationID: conversationID, conversationDomain: conversationDomain)) - guard let mock = storeFailedConversationWithQualifiedId_MockMethod else { - fatalError("no mock for `storeFailedConversationWithQualifiedId`") + guard let mock = storeFailedConversationConversationIDConversationDomain_MockMethod else { + fatalError("no mock for `storeFailedConversationConversationIDConversationDomain`") } - await mock(qualifiedId) + await mock(conversationID, conversationDomain) } // MARK: - fetchMLSConversation @@ -507,10 +507,10 @@ public class MockConversationRepositoryProtocol: ConversationRepositoryProtocol // MARK: - storeConversation - public var storeConversationTimestamp_Invocations: [(conversation: WireAPI.Conversation, timestamp: Date)] = [] - public var storeConversationTimestamp_MockMethod: ((WireAPI.Conversation, Date) async -> Void)? + public var storeConversationTimestamp_Invocations: [(conversation: WireDomain.Conversation, timestamp: Date)] = [] + public var storeConversationTimestamp_MockMethod: ((WireDomain.Conversation, Date) async -> Void)? - public func storeConversation(_ conversation: WireAPI.Conversation, timestamp: Date) async { + public func storeConversation(_ conversation: WireDomain.Conversation, timestamp: Date) async { storeConversationTimestamp_Invocations.append((conversation: conversation, timestamp: timestamp)) guard let mock = storeConversationTimestamp_MockMethod else { diff --git a/WireDomain/Tests/WireDomainTests/Repositories/ConversationRepositoryTests.swift b/WireDomain/Tests/WireDomainTests/Repositories/ConversationRepositoryTests.swift index cc6ac335603..4a4f7d58ac4 100644 --- a/WireDomain/Tests/WireDomainTests/Repositories/ConversationRepositoryTests.swift +++ b/WireDomain/Tests/WireDomainTests/Repositories/ConversationRepositoryTests.swift @@ -391,7 +391,7 @@ final class ConversationRepositoryTests: XCTestCase { // When - await sut.storeConversation(Scaffolding.conversationGroupType, timestamp: .now) + await sut.storeConversation(Scaffolding.conversationGroupType.toDomainModel(), timestamp: .now) // Then From 0e123e46dd5bbe2453e695ffb46892d8c4db7458 Mon Sep 17 00:00:00 2001 From: Jullian Mercier <31648126+jullianm@users.noreply.github.com> Date: Tue, 26 Nov 2024 16:11:01 +0100 Subject: [PATCH 3/9] fix remaining conflict issue - WPB-10171 (#2098) --- .../ConversationMessageTimerUpdateEventProcessor.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/WireDomain/Sources/WireDomain/Event Processing/ConversationEventProcessor/ConversationMessageTimerUpdateEventProcessor.swift b/WireDomain/Sources/WireDomain/Event Processing/ConversationEventProcessor/ConversationMessageTimerUpdateEventProcessor.swift index 192361835ae..7d46c46f7f5 100644 --- a/WireDomain/Sources/WireDomain/Event Processing/ConversationEventProcessor/ConversationMessageTimerUpdateEventProcessor.swift +++ b/WireDomain/Sources/WireDomain/Event Processing/ConversationEventProcessor/ConversationMessageTimerUpdateEventProcessor.swift @@ -44,12 +44,12 @@ struct ConversationMessageTimerUpdateEventProcessor: ConversationMessageTimerUpd let timestamp = event.timestamp let sender = await userRepository.fetchOrCreateUser( - with: userID.uuid, + id: userID.uuid, domain: userID.domain ) let conversation = await conversationRepository.fetchOrCreateConversation( - with: conversationID.uuid, + id: conversationID.uuid, domain: conversationID.domain ) From ff335a6c06d24bdbda7bb70a089eb9078bea905d Mon Sep 17 00:00:00 2001 From: Jullian Mercier <31648126+jullianm@users.noreply.github.com> Date: Tue, 26 Nov 2024 16:28:08 +0100 Subject: [PATCH 4/9] add system message type and call dedicated method from message repository, add UTs, clean up code - WPB-10171 (#2098) --- .../project.pbxproj | 12 ++++++--- .../ConversationEventProcessor.swift | 6 ++--- ...tionMessageTimerUpdateEventProcessor.swift | 27 ++++++++----------- .../Message/MessageLocalStore.swift | 19 +++++++++++++ .../Message/Models/MessageType.swift | 6 +++++ ...essageTimerUpdateEventProcessorTests.swift | 21 ++++++++------- .../LocalStores/MessageLocalStoreTests.swift | 2 ++ 7 files changed, 60 insertions(+), 33 deletions(-) diff --git a/WireDomain/Project/WireDomain Project.xcodeproj/project.pbxproj b/WireDomain/Project/WireDomain Project.xcodeproj/project.pbxproj index 73c55f45604..9ec4f6f75f2 100644 --- a/WireDomain/Project/WireDomain Project.xcodeproj/project.pbxproj +++ b/WireDomain/Project/WireDomain Project.xcodeproj/project.pbxproj @@ -86,6 +86,8 @@ C9C8FDD32C9DBE0E00702B91 /* UserLegalHoldDisableEventProcessorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9C8FDCA2C9DBE0E00702B91 /* UserLegalHoldDisableEventProcessorTests.swift */; }; C9C8FDD42C9DBE0E00702B91 /* UserLegalholdRequestEventProcessorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9C8FDCB2C9DBE0E00702B91 /* UserLegalholdRequestEventProcessorTests.swift */; }; C9D5793F2CF618C600012A0E /* Conversation.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9D5793E2CF618C600012A0E /* Conversation.swift */; }; + C9D579422CF61F2300012A0E /* ConversationMessageTimerUpdateEventProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9D579412CF61F2300012A0E /* ConversationMessageTimerUpdateEventProcessor.swift */; }; + C9D579442CF61F6D00012A0E /* ConversationMessageTimerUpdateEventProcessorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9D579432CF61F6D00012A0E /* ConversationMessageTimerUpdateEventProcessorTests.swift */; }; C9E0C9BB2C91B76F00CE6607 /* WireTestingPackage in Frameworks */ = {isa = PBXBuildFile; productRef = C9E0C9BA2C91B76F00CE6607 /* WireTestingPackage */; }; C9E8A3AE2C73878B0093DD5C /* ConnectionsRepositoryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9E8A3AD2C73878B0093DD5C /* ConnectionsRepositoryTests.swift */; }; C9E8A3B72C749F2A0093DD5C /* ConversationLabelsRepositoryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9E8A3B62C749F2A0093DD5C /* ConversationLabelsRepositoryTests.swift */; }; @@ -112,7 +114,6 @@ EEAD09FA2C46773300CC8658 /* ConversationMemberJoinEventProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEAD09F92C46773300CC8658 /* ConversationMemberJoinEventProcessor.swift */; }; EEAD09FC2C46773900CC8658 /* ConversationMemberLeaveEventProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEAD09FB2C46773900CC8658 /* ConversationMemberLeaveEventProcessor.swift */; }; EEAD09FE2C46774200CC8658 /* ConversationMemberUpdateEventProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEAD09FD2C46774200CC8658 /* ConversationMemberUpdateEventProcessor.swift */; }; - EEAD0A002C46774900CC8658 /* ConversationMessageTimerUpdateEventProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEAD09FF2C46774900CC8658 /* ConversationMessageTimerUpdateEventProcessor.swift */; }; EEAD0A022C46774F00CC8658 /* ConversationMLSMessageAddEventProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEAD0A012C46774F00CC8658 /* ConversationMLSMessageAddEventProcessor.swift */; }; EEAD0A042C46775700CC8658 /* ConversationMLSWelcomeEventProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEAD0A032C46775700CC8658 /* ConversationMLSWelcomeEventProcessor.swift */; }; EEAD0A062C46775D00CC8658 /* ConversationProteusMessageAddEventProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEAD0A052C46775D00CC8658 /* ConversationProteusMessageAddEventProcessor.swift */; }; @@ -237,6 +238,8 @@ C9C8FDCA2C9DBE0E00702B91 /* UserLegalHoldDisableEventProcessorTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserLegalHoldDisableEventProcessorTests.swift; sourceTree = ""; }; C9C8FDCB2C9DBE0E00702B91 /* UserLegalholdRequestEventProcessorTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserLegalholdRequestEventProcessorTests.swift; sourceTree = ""; }; C9D5793E2CF618C600012A0E /* Conversation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Conversation.swift; sourceTree = ""; }; + C9D579412CF61F2300012A0E /* ConversationMessageTimerUpdateEventProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversationMessageTimerUpdateEventProcessor.swift; sourceTree = ""; }; + C9D579432CF61F6D00012A0E /* ConversationMessageTimerUpdateEventProcessorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversationMessageTimerUpdateEventProcessorTests.swift; sourceTree = ""; }; C9E8A3AD2C73878B0093DD5C /* ConnectionsRepositoryTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectionsRepositoryTests.swift; sourceTree = ""; }; C9E8A3B62C749F2A0093DD5C /* ConversationLabelsRepositoryTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConversationLabelsRepositoryTests.swift; sourceTree = ""; }; C9E8A3BF2C761EDD0093DD5C /* FeatureConfigRepositoryTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeatureConfigRepositoryTests.swift; sourceTree = ""; }; @@ -265,7 +268,6 @@ EEAD09F92C46773300CC8658 /* ConversationMemberJoinEventProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversationMemberJoinEventProcessor.swift; sourceTree = ""; }; EEAD09FB2C46773900CC8658 /* ConversationMemberLeaveEventProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversationMemberLeaveEventProcessor.swift; sourceTree = ""; }; EEAD09FD2C46774200CC8658 /* ConversationMemberUpdateEventProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversationMemberUpdateEventProcessor.swift; sourceTree = ""; }; - EEAD09FF2C46774900CC8658 /* ConversationMessageTimerUpdateEventProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversationMessageTimerUpdateEventProcessor.swift; sourceTree = ""; }; EEAD0A012C46774F00CC8658 /* ConversationMLSMessageAddEventProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversationMLSMessageAddEventProcessor.swift; sourceTree = ""; }; EEAD0A032C46775700CC8658 /* ConversationMLSWelcomeEventProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversationMLSWelcomeEventProcessor.swift; sourceTree = ""; }; EEAD0A052C46775D00CC8658 /* ConversationProteusMessageAddEventProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversationProteusMessageAddEventProcessor.swift; sourceTree = ""; }; @@ -478,6 +480,7 @@ C96B75442CDBA0A9003A85EB /* ConversationDeleteEventProcessorTests.swift */, C96B75232CDB7688003A85EB /* ConversationMemberJoinEventTests.swift */, C96B75242CDB7688003A85EB /* ConversationMemberLeaveEventTests.swift */, + C9D579432CF61F6D00012A0E /* ConversationMessageTimerUpdateEventProcessorTests.swift */, C96B75252CDB7688003A85EB /* ConversationMemberUpdateEventProcessorTests.swift */, C96B75262CDB7688003A85EB /* ConversationProtocolUpdateEventProcessorTests.swift */, C96B75272CDB7688003A85EB /* ConversationReceiptModeUpdateEventProcessorTests.swift */, @@ -744,7 +747,7 @@ EEAD09F92C46773300CC8658 /* ConversationMemberJoinEventProcessor.swift */, EEAD09FB2C46773900CC8658 /* ConversationMemberLeaveEventProcessor.swift */, EEAD09FD2C46774200CC8658 /* ConversationMemberUpdateEventProcessor.swift */, - EEAD09FF2C46774900CC8658 /* ConversationMessageTimerUpdateEventProcessor.swift */, + C9D579412CF61F2300012A0E /* ConversationMessageTimerUpdateEventProcessor.swift */, EEAD0A012C46774F00CC8658 /* ConversationMLSMessageAddEventProcessor.swift */, EEAD0A032C46775700CC8658 /* ConversationMLSWelcomeEventProcessor.swift */, EEAD0A052C46775D00CC8658 /* ConversationProteusMessageAddEventProcessor.swift */, @@ -1050,11 +1053,11 @@ EE57A7082C2A8B740096F242 /* ProteusMessageDecryptorError.swift in Sources */, EEAD0A1A2C46A92000CC8658 /* UserClientRemoveEventProcessor.swift in Sources */, C98433E92CCA86CA009723D4 /* MessageLocalStore.swift in Sources */, + C9D579422CF61F2300012A0E /* ConversationMessageTimerUpdateEventProcessor.swift in Sources */, EEAD0A0A2C46776B00CC8658 /* ConversationReceiptModeUpdateEventProcessor.swift in Sources */, C99322D62C986E3A0065E10F /* UserRepository.swift in Sources */, C9C102442CE789F500EA273F /* OneOnOneResolver.swift in Sources */, C98433EF2CCB7EE6009723D4 /* MessageType.swift in Sources */, - EEAD0A002C46774900CC8658 /* ConversationMessageTimerUpdateEventProcessor.swift in Sources */, EEAD0A182C46A88D00CC8658 /* UserClientAddEventProcessor.swift in Sources */, C99322DC2C986E3A0065E10F /* FeatureConfigRepository.swift in Sources */, C99322E22C986E3A0065E10F /* ConversationLocalStore+Status.swift in Sources */, @@ -1108,6 +1111,7 @@ C96B752A2CDB7688003A85EB /* ConversationMemberJoinEventTests.swift in Sources */, C96B752B2CDB7688003A85EB /* ConversationProtocolUpdateEventProcessorTests.swift in Sources */, C96B752C2CDB7688003A85EB /* ConversationAccessUpdateEventProcessorTests.swift in Sources */, + C9D579442CF61F6D00012A0E /* ConversationMessageTimerUpdateEventProcessorTests.swift in Sources */, C96B752D2CDB7688003A85EB /* ConversationCreateEventProcessorTests.swift in Sources */, C96B752E2CDB7688003A85EB /* ConversationReceiptModeUpdateEventProcessorTests.swift in Sources */, C96B752F2CDB7688003A85EB /* ConversationMemberLeaveEventTests.swift in Sources */, diff --git a/WireDomain/Sources/WireDomain/Event Processing/ConversationEventProcessor/ConversationEventProcessor.swift b/WireDomain/Sources/WireDomain/Event Processing/ConversationEventProcessor/ConversationEventProcessor.swift index cf0dd124f34..143ec09aa64 100644 --- a/WireDomain/Sources/WireDomain/Event Processing/ConversationEventProcessor/ConversationEventProcessor.swift +++ b/WireDomain/Sources/WireDomain/Event Processing/ConversationEventProcessor/ConversationEventProcessor.swift @@ -54,13 +54,13 @@ struct ConversationEventProcessor { func processEvent(_ event: ConversationEvent) async throws { switch event { case let .accessUpdate(event): - try await accessUpdateEventProcessor.processEvent(event) + await accessUpdateEventProcessor.processEvent(event) case let .codeUpdate(event): try await codeUpdateEventProcessor.processEvent(event) case let .create(event): - try await createEventProcessor.processEvent(event) + await createEventProcessor.processEvent(event) case let .delete(event): try await deleteEventProcessor.processEvent(event) @@ -75,7 +75,7 @@ struct ConversationEventProcessor { try await memberUpdateEventProcessor.processEvent(event) case let .messageTimerUpdate(event): - try await messageTimerUpdateEventProcessor.processEvent(event) + await messageTimerUpdateEventProcessor.processEvent(event) case let .mlsMessageAdd(event): try await mlsMessageAddEventProcessor.processEvent(event) diff --git a/WireDomain/Sources/WireDomain/Event Processing/ConversationEventProcessor/ConversationMessageTimerUpdateEventProcessor.swift b/WireDomain/Sources/WireDomain/Event Processing/ConversationEventProcessor/ConversationMessageTimerUpdateEventProcessor.swift index 7d46c46f7f5..a3507140777 100644 --- a/WireDomain/Sources/WireDomain/Event Processing/ConversationEventProcessor/ConversationMessageTimerUpdateEventProcessor.swift +++ b/WireDomain/Sources/WireDomain/Event Processing/ConversationEventProcessor/ConversationMessageTimerUpdateEventProcessor.swift @@ -36,6 +36,7 @@ struct ConversationMessageTimerUpdateEventProcessor: ConversationMessageTimerUpd let userRepository: any UserRepositoryProtocol let conversationRepository: any ConversationRepositoryProtocol let conversationLocalStore: any ConversationLocalStoreProtocol + let messageRepository: any MessageRepositoryProtocol func processEvent(_ event: ConversationMessageTimerUpdateEvent) async { let userID = event.senderID @@ -43,11 +44,6 @@ struct ConversationMessageTimerUpdateEventProcessor: ConversationMessageTimerUpd let timer = Double(event.newTimer ?? 0) let timestamp = event.timestamp - let sender = await userRepository.fetchOrCreateUser( - id: userID.uuid, - domain: userID.domain - ) - let conversation = await conversationRepository.fetchOrCreateConversation( id: conversationID.uuid, domain: conversationID.domain @@ -58,18 +54,17 @@ struct ConversationMessageTimerUpdateEventProcessor: ConversationMessageTimerUpd let currentTimeout = await conversationLocalStore.conversationMessageDestructionTimeout(conversation) if currentTimeout != timeout { - let systemMessage = SystemMessage( - type: .messageTimerUpdate, - sender: sender, - users: [sender], - timestamp: timestamp, - messageTimer: timeoutValue + + let messageType: MessageType = .messageTimerUpdate( + sender: (userID.uuid, userID.domain), + date: timestamp, + timeoutValue: timeoutValue ) - - // TODO: [WPB-11839] Use MessageRepository - await conversationRepository.addSystemMessage( - systemMessage, - to: conversation + + await messageRepository.addMessageToConversation( + messageType: messageType, + conversationID: conversationID.uuid, + conversationDomain: conversationID.domain ) } diff --git a/WireDomain/Sources/WireDomain/Repositories/Message/MessageLocalStore.swift b/WireDomain/Sources/WireDomain/Repositories/Message/MessageLocalStore.swift index df7e5860432..1ff09d5b261 100644 --- a/WireDomain/Sources/WireDomain/Repositories/Message/MessageLocalStore.swift +++ b/WireDomain/Sources/WireDomain/Repositories/Message/MessageLocalStore.swift @@ -313,6 +313,25 @@ public final class MessageLocalStore: MessageLocalStoreProtocol { ) return [systemMessage] + + case .messageTimerUpdate(let sender, let date, let timeoutValue): + guard let sender = await fetchUser( + id: sender.id, + domain: sender.domain + ) else { + return [] + } + + let systemMessage = await createSystemMessage( + messageType: .messageTimerUpdate, + sender: sender, + users: [sender], + timestamp: date, + messageTimer: timeoutValue + ) + + return [systemMessage] + } } diff --git a/WireDomain/Sources/WireDomain/Repositories/Message/Models/MessageType.swift b/WireDomain/Sources/WireDomain/Repositories/Message/Models/MessageType.swift index 352e197b3e4..77b46e7be4f 100644 --- a/WireDomain/Sources/WireDomain/Repositories/Message/Models/MessageType.swift +++ b/WireDomain/Sources/WireDomain/Repositories/Message/Models/MessageType.swift @@ -68,4 +68,10 @@ public enum MessageType: Sendable { case receiptModeIsOn( date: Date ) + + case messageTimerUpdate( + sender: (id: UUID, domain: String?), + date: Date, + timeoutValue: Double + ) } diff --git a/WireDomain/Tests/WireDomainTests/Event Processing/ConversationEventProcessor/ConversationMessageTimerUpdateEventProcessorTests.swift b/WireDomain/Tests/WireDomainTests/Event Processing/ConversationEventProcessor/ConversationMessageTimerUpdateEventProcessorTests.swift index 71fa987d829..ad39d93b85b 100644 --- a/WireDomain/Tests/WireDomainTests/Event Processing/ConversationEventProcessor/ConversationMessageTimerUpdateEventProcessorTests.swift +++ b/WireDomain/Tests/WireDomainTests/Event Processing/ConversationEventProcessor/ConversationMessageTimerUpdateEventProcessorTests.swift @@ -29,6 +29,7 @@ final class ConversationMessageTimerUpdateEventProcessorTests: XCTestCase { private var userRepository: MockUserRepositoryProtocol! private var conversationRepository: MockConversationRepositoryProtocol! private var conversationLocalStore: MockConversationLocalStoreProtocol! + private var messageRepository: MockMessageRepositoryProtocol! private var coreDataStack: CoreDataStack! private var coreDataStackHelper: CoreDataStackHelper! private var modelHelper: ModelHelper! @@ -47,11 +48,13 @@ final class ConversationMessageTimerUpdateEventProcessorTests: XCTestCase { userRepository = MockUserRepositoryProtocol() conversationRepository = MockConversationRepositoryProtocol() conversationLocalStore = MockConversationLocalStoreProtocol() + messageRepository = MockMessageRepositoryProtocol() sut = ConversationMessageTimerUpdateEventProcessor( userRepository: userRepository, conversationRepository: conversationRepository, - conversationLocalStore: conversationLocalStore + conversationLocalStore: conversationLocalStore, + messageRepository: messageRepository ) } @@ -61,6 +64,7 @@ final class ConversationMessageTimerUpdateEventProcessorTests: XCTestCase { userRepository = nil conversationRepository = nil conversationLocalStore = nil + messageRepository = nil modelHelper = nil coreDataStack = nil try coreDataStackHelper.cleanupDirectory() @@ -72,18 +76,16 @@ final class ConversationMessageTimerUpdateEventProcessorTests: XCTestCase { func testProcessEvent_It_Invokes_Repo_Methods() async { // Mock - let (user, conversation) = await context.perform { [self] in - let user = modelHelper.createUser(in: context) + let (conversation) = await context.perform { [self] in let conversation = modelHelper.createGroupConversation(in: context) - return (user, conversation) + return (conversation) } - userRepository.fetchOrCreateUserWithDomain_MockValue = user - conversationRepository.fetchOrCreateConversationWithDomain_MockValue = conversation - conversationRepository.addSystemMessageTo_MockMethod = { _, _ in } + conversationRepository.fetchOrCreateConversationIdDomain_MockValue = conversation conversationLocalStore.conversationMessageDestructionTimeout_MockValue = .fiveMinutes conversationLocalStore.storeConversationTimeoutValueFor_MockMethod = { _, _ in } + messageRepository.addMessageToConversationMessageTypeConversationIDConversationDomain_MockMethod = { _, _, _ in } // When @@ -91,11 +93,10 @@ final class ConversationMessageTimerUpdateEventProcessorTests: XCTestCase { // Then - XCTAssertEqual(userRepository.fetchOrCreateUserWithDomain_Invocations.count, 1) - XCTAssertEqual(conversationRepository.fetchOrCreateConversationWithDomain_Invocations.count, 1) - XCTAssertEqual(conversationRepository.addSystemMessageTo_Invocations.count, 1) + XCTAssertEqual(conversationRepository.fetchOrCreateConversationIdDomain_Invocations.count, 1) XCTAssertEqual(conversationLocalStore.conversationMessageDestructionTimeout_Invocations.count, 1) XCTAssertEqual(conversationLocalStore.storeConversationTimeoutValueFor_Invocations.count, 1) + XCTAssertEqual(messageRepository.addMessageToConversationMessageTypeConversationIDConversationDomain_Invocations.count, 1) } private enum Scaffolding { diff --git a/WireDomain/Tests/WireDomainTests/LocalStores/MessageLocalStoreTests.swift b/WireDomain/Tests/WireDomainTests/LocalStores/MessageLocalStoreTests.swift index 9a93bce5a9b..79732f946e2 100644 --- a/WireDomain/Tests/WireDomainTests/LocalStores/MessageLocalStoreTests.swift +++ b/WireDomain/Tests/WireDomainTests/LocalStores/MessageLocalStoreTests.swift @@ -138,6 +138,8 @@ final class MessageLocalStoreTests: XCTestCase { (messagesCount: 1, [.mlsMigrationFinalized]) case .receiptModeIsOn: (messagesCount: 1, [.readReceiptsOn]) + case .messageTimerUpdate: + (messagesCount: 1, [.messageTimerUpdate]) } } From 768b81bedd1d405982b1215eb640cd0379c96142 Mon Sep 17 00:00:00 2001 From: Jullian Mercier <31648126+jullianm@users.noreply.github.com> Date: Tue, 26 Nov 2024 16:28:46 +0100 Subject: [PATCH 5/9] lint and format - WPB-10171 (#2098) --- ...ationMessageTimerUpdateEventProcessor.swift | 6 +++--- .../Conversations/ConversationLocalStore.swift | 3 ++- .../Message/MessageLocalStore.swift | 9 ++++----- .../Message/Models/MessageType.swift | 2 +- ...MessageTimerUpdateEventProcessorTests.swift | 18 ++++++++++-------- ...sationCreationControllerSnapshotTests.swift | 3 ++- .../ConversationCreationController.swift | 14 +++++++------- .../Services/ServiceDetailViewController.swift | 6 +++--- .../StartUI/StartUIViewController.swift | 4 ++-- 9 files changed, 34 insertions(+), 31 deletions(-) diff --git a/WireDomain/Sources/WireDomain/Event Processing/ConversationEventProcessor/ConversationMessageTimerUpdateEventProcessor.swift b/WireDomain/Sources/WireDomain/Event Processing/ConversationEventProcessor/ConversationMessageTimerUpdateEventProcessor.swift index a3507140777..4df32e76d86 100644 --- a/WireDomain/Sources/WireDomain/Event Processing/ConversationEventProcessor/ConversationMessageTimerUpdateEventProcessor.swift +++ b/WireDomain/Sources/WireDomain/Event Processing/ConversationEventProcessor/ConversationMessageTimerUpdateEventProcessor.swift @@ -49,18 +49,18 @@ struct ConversationMessageTimerUpdateEventProcessor: ConversationMessageTimerUpd domain: conversationID.domain ) - let timeoutValue = timer / 1_000 + let timeoutValue = timer / 1000 let timeout: MessageDestructionTimeoutValue = .init(rawValue: timeoutValue) let currentTimeout = await conversationLocalStore.conversationMessageDestructionTimeout(conversation) if currentTimeout != timeout { - + let messageType: MessageType = .messageTimerUpdate( sender: (userID.uuid, userID.domain), date: timestamp, timeoutValue: timeoutValue ) - + await messageRepository.addMessageToConversation( messageType: messageType, conversationID: conversationID.uuid, diff --git a/WireDomain/Sources/WireDomain/Repositories/Conversations/ConversationLocalStore.swift b/WireDomain/Sources/WireDomain/Repositories/Conversations/ConversationLocalStore.swift index b335de157f3..21896fff5ce 100644 --- a/WireDomain/Sources/WireDomain/Repositories/Conversations/ConversationLocalStore.swift +++ b/WireDomain/Sources/WireDomain/Repositories/Conversations/ConversationLocalStore.swift @@ -54,7 +54,8 @@ public protocol ConversationLocalStoreProtocol { ) async /// Stores a flag indicating whether a conversation requires an update from backend. - /// - Parameter needsBackendUpdate: A flag indicated whether the qualified conversation needs to be updated from backend. + /// - Parameter needsBackendUpdate: A flag indicated whether the qualified conversation needs to be updated from + /// backend. /// - Parameter conversationID: The conversation ID. /// - Parameter conversationDomain: The conversation domain. diff --git a/WireDomain/Sources/WireDomain/Repositories/Message/MessageLocalStore.swift b/WireDomain/Sources/WireDomain/Repositories/Message/MessageLocalStore.swift index 1ff09d5b261..17d3fe306a0 100644 --- a/WireDomain/Sources/WireDomain/Repositories/Message/MessageLocalStore.swift +++ b/WireDomain/Sources/WireDomain/Repositories/Message/MessageLocalStore.swift @@ -313,15 +313,15 @@ public final class MessageLocalStore: MessageLocalStoreProtocol { ) return [systemMessage] - - case .messageTimerUpdate(let sender, let date, let timeoutValue): + + case let .messageTimerUpdate(sender, date, timeoutValue): guard let sender = await fetchUser( id: sender.id, domain: sender.domain ) else { return [] } - + let systemMessage = await createSystemMessage( messageType: .messageTimerUpdate, sender: sender, @@ -329,9 +329,8 @@ public final class MessageLocalStore: MessageLocalStoreProtocol { timestamp: date, messageTimer: timeoutValue ) - + return [systemMessage] - } } diff --git a/WireDomain/Sources/WireDomain/Repositories/Message/Models/MessageType.swift b/WireDomain/Sources/WireDomain/Repositories/Message/Models/MessageType.swift index 77b46e7be4f..c42e926e92f 100644 --- a/WireDomain/Sources/WireDomain/Repositories/Message/Models/MessageType.swift +++ b/WireDomain/Sources/WireDomain/Repositories/Message/Models/MessageType.swift @@ -68,7 +68,7 @@ public enum MessageType: Sendable { case receiptModeIsOn( date: Date ) - + case messageTimerUpdate( sender: (id: UUID, domain: String?), date: Date, diff --git a/WireDomain/Tests/WireDomainTests/Event Processing/ConversationEventProcessor/ConversationMessageTimerUpdateEventProcessorTests.swift b/WireDomain/Tests/WireDomainTests/Event Processing/ConversationEventProcessor/ConversationMessageTimerUpdateEventProcessorTests.swift index ad39d93b85b..f643669647f 100644 --- a/WireDomain/Tests/WireDomainTests/Event Processing/ConversationEventProcessor/ConversationMessageTimerUpdateEventProcessorTests.swift +++ b/WireDomain/Tests/WireDomainTests/Event Processing/ConversationEventProcessor/ConversationMessageTimerUpdateEventProcessorTests.swift @@ -16,12 +16,12 @@ // along with this program. If not, see http://www.gnu.org/licenses/. // -@testable import WireAPI import WireDataModel import WireDataModelSupport -@testable import WireDomain import WireDomainSupport import XCTest +@testable import WireAPI +@testable import WireDomain final class ConversationMessageTimerUpdateEventProcessorTests: XCTestCase { @@ -76,16 +76,15 @@ final class ConversationMessageTimerUpdateEventProcessorTests: XCTestCase { func testProcessEvent_It_Invokes_Repo_Methods() async { // Mock - let (conversation) = await context.perform { [self] in - let conversation = modelHelper.createGroupConversation(in: context) - - return (conversation) + let conversation = await context.perform { [self] in + return modelHelper.createGroupConversation(in: context) } conversationRepository.fetchOrCreateConversationIdDomain_MockValue = conversation conversationLocalStore.conversationMessageDestructionTimeout_MockValue = .fiveMinutes conversationLocalStore.storeConversationTimeoutValueFor_MockMethod = { _, _ in } - messageRepository.addMessageToConversationMessageTypeConversationIDConversationDomain_MockMethod = { _, _, _ in } + messageRepository + .addMessageToConversationMessageTypeConversationIDConversationDomain_MockMethod = { _, _, _ in } // When @@ -96,7 +95,10 @@ final class ConversationMessageTimerUpdateEventProcessorTests: XCTestCase { XCTAssertEqual(conversationRepository.fetchOrCreateConversationIdDomain_Invocations.count, 1) XCTAssertEqual(conversationLocalStore.conversationMessageDestructionTimeout_Invocations.count, 1) XCTAssertEqual(conversationLocalStore.storeConversationTimeoutValueFor_Invocations.count, 1) - XCTAssertEqual(messageRepository.addMessageToConversationMessageTypeConversationIDConversationDomain_Invocations.count, 1) + XCTAssertEqual( + messageRepository.addMessageToConversationMessageTypeConversationIDConversationDomain_Invocations.count, + 1 + ) } private enum Scaffolding { diff --git a/wire-ios/Wire-iOS Tests/ConversationCreationControllerSnapshotTests.swift b/wire-ios/Wire-iOS Tests/ConversationCreationControllerSnapshotTests.swift index b7d0ee33519..01917a4ca94 100644 --- a/wire-ios/Wire-iOS Tests/ConversationCreationControllerSnapshotTests.swift +++ b/wire-ios/Wire-iOS Tests/ConversationCreationControllerSnapshotTests.swift @@ -92,6 +92,7 @@ final class ConversationCreationControllerSnapshotTests: XCTestCase { sut = ConversationCreationController( preSelectedParticipants: nil, userSession: mockUserSession, - mlsFeature: mlsFeature) + mlsFeature: mlsFeature + ) } } diff --git a/wire-ios/Wire-iOS/Sources/UserInterface/Conversation/Create/ConversationCreationController.swift b/wire-ios/Wire-iOS/Sources/UserInterface/Conversation/Create/ConversationCreationController.swift index c7cda38aae3..eadd1c2f570 100644 --- a/wire-ios/Wire-iOS/Sources/UserInterface/Conversation/Create/ConversationCreationController.swift +++ b/wire-ios/Wire-iOS/Sources/UserInterface/Conversation/Create/ConversationCreationController.swift @@ -123,10 +123,10 @@ final class ConversationCreationController: UIViewController { self.presentEncryptionProtocolPicker(sender: sender) { [weak self] encryptionProtocol in guard let self else { return } - self.values.encryptionProtocol = encryptionProtocol - self.updateOptions() + values.encryptionProtocol = encryptionProtocol + updateOptions() - self.reloadOptionsSections() + reloadOptionsSections() } } return section @@ -139,7 +139,7 @@ final class ConversationCreationController: UIViewController { // ignoring the conversation name so we don't loose the info while testing let excludedSectionIndex = collectionViewController.sections.startIndex let endIndex = collectionView.numberOfSections - let sectionsToReload = IndexSet(integersIn: (excludedSectionIndex + 1).. 1 && - userSession.selfUser.canSeeServices && - userSession.mlsFeature.config.defaultProtocol != .mls + userSession.selfUser.canSeeServices && + userSession.mlsFeature.config.defaultProtocol != .mls } // MARK: - Init From 25221ebfb5d7c61107b47436cd2fc321cb72924a Mon Sep 17 00:00:00 2001 From: Jullian Mercier <31648126+jullianm@users.noreply.github.com> Date: Wed, 27 Nov 2024 16:48:06 +0100 Subject: [PATCH 6/9] rename timer property, remove conversationRepository from processor - WPB-10171 (#2098) --- .../ConversationMessageTimerUpdateEventProcessor.swift | 7 +++---- ...onversationMessageTimerUpdateEventProcessorTests.swift | 8 ++------ 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/WireDomain/Sources/WireDomain/Event Processing/ConversationEventProcessor/ConversationMessageTimerUpdateEventProcessor.swift b/WireDomain/Sources/WireDomain/Event Processing/ConversationEventProcessor/ConversationMessageTimerUpdateEventProcessor.swift index 4df32e76d86..afa3b1a6949 100644 --- a/WireDomain/Sources/WireDomain/Event Processing/ConversationEventProcessor/ConversationMessageTimerUpdateEventProcessor.swift +++ b/WireDomain/Sources/WireDomain/Event Processing/ConversationEventProcessor/ConversationMessageTimerUpdateEventProcessor.swift @@ -34,22 +34,21 @@ protocol ConversationMessageTimerUpdateEventProcessorProtocol { struct ConversationMessageTimerUpdateEventProcessor: ConversationMessageTimerUpdateEventProcessorProtocol { let userRepository: any UserRepositoryProtocol - let conversationRepository: any ConversationRepositoryProtocol let conversationLocalStore: any ConversationLocalStoreProtocol let messageRepository: any MessageRepositoryProtocol func processEvent(_ event: ConversationMessageTimerUpdateEvent) async { let userID = event.senderID let conversationID = event.conversationID - let timer = Double(event.newTimer ?? 0) + let timerInMilliseconds = Double(event.newTimer ?? 0) let timestamp = event.timestamp - let conversation = await conversationRepository.fetchOrCreateConversation( + let conversation = await conversationLocalStore.fetchOrCreateConversation( id: conversationID.uuid, domain: conversationID.domain ) - let timeoutValue = timer / 1000 + let timeoutValue = timerInMilliseconds / 1000 let timeout: MessageDestructionTimeoutValue = .init(rawValue: timeoutValue) let currentTimeout = await conversationLocalStore.conversationMessageDestructionTimeout(conversation) diff --git a/WireDomain/Tests/WireDomainTests/Event Processing/ConversationEventProcessor/ConversationMessageTimerUpdateEventProcessorTests.swift b/WireDomain/Tests/WireDomainTests/Event Processing/ConversationEventProcessor/ConversationMessageTimerUpdateEventProcessorTests.swift index f643669647f..98def73b0d1 100644 --- a/WireDomain/Tests/WireDomainTests/Event Processing/ConversationEventProcessor/ConversationMessageTimerUpdateEventProcessorTests.swift +++ b/WireDomain/Tests/WireDomainTests/Event Processing/ConversationEventProcessor/ConversationMessageTimerUpdateEventProcessorTests.swift @@ -27,7 +27,6 @@ final class ConversationMessageTimerUpdateEventProcessorTests: XCTestCase { private var sut: ConversationMessageTimerUpdateEventProcessor! private var userRepository: MockUserRepositoryProtocol! - private var conversationRepository: MockConversationRepositoryProtocol! private var conversationLocalStore: MockConversationLocalStoreProtocol! private var messageRepository: MockMessageRepositoryProtocol! private var coreDataStack: CoreDataStack! @@ -46,13 +45,11 @@ final class ConversationMessageTimerUpdateEventProcessorTests: XCTestCase { coreDataStack = try await coreDataStackHelper.createStack() userRepository = MockUserRepositoryProtocol() - conversationRepository = MockConversationRepositoryProtocol() conversationLocalStore = MockConversationLocalStoreProtocol() messageRepository = MockMessageRepositoryProtocol() sut = ConversationMessageTimerUpdateEventProcessor( userRepository: userRepository, - conversationRepository: conversationRepository, conversationLocalStore: conversationLocalStore, messageRepository: messageRepository ) @@ -62,7 +59,6 @@ final class ConversationMessageTimerUpdateEventProcessorTests: XCTestCase { try await super.tearDown() sut = nil userRepository = nil - conversationRepository = nil conversationLocalStore = nil messageRepository = nil modelHelper = nil @@ -80,7 +76,7 @@ final class ConversationMessageTimerUpdateEventProcessorTests: XCTestCase { return modelHelper.createGroupConversation(in: context) } - conversationRepository.fetchOrCreateConversationIdDomain_MockValue = conversation + conversationLocalStore.fetchOrCreateConversationIdDomain_MockValue = conversation conversationLocalStore.conversationMessageDestructionTimeout_MockValue = .fiveMinutes conversationLocalStore.storeConversationTimeoutValueFor_MockMethod = { _, _ in } messageRepository @@ -92,7 +88,7 @@ final class ConversationMessageTimerUpdateEventProcessorTests: XCTestCase { // Then - XCTAssertEqual(conversationRepository.fetchOrCreateConversationIdDomain_Invocations.count, 1) + XCTAssertEqual(conversationLocalStore.fetchOrCreateConversationIdDomain_Invocations.count, 1) XCTAssertEqual(conversationLocalStore.conversationMessageDestructionTimeout_Invocations.count, 1) XCTAssertEqual(conversationLocalStore.storeConversationTimeoutValueFor_Invocations.count, 1) XCTAssertEqual( From 36461566dd6fbe0e4de522b111e9ddb63e5d7214 Mon Sep 17 00:00:00 2001 From: Jullian Mercier <31648126+jullianm@users.noreply.github.com> Date: Wed, 27 Nov 2024 17:04:16 +0100 Subject: [PATCH 7/9] use MessageLocalStore to add system message - WPB-10171 (#2098) --- ...ationMessageTimerUpdateEventProcessor.swift | 5 ++--- ...MessageTimerUpdateEventProcessorTests.swift | 18 +++++++----------- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/WireDomain/Sources/WireDomain/Event Processing/ConversationEventProcessor/ConversationMessageTimerUpdateEventProcessor.swift b/WireDomain/Sources/WireDomain/Event Processing/ConversationEventProcessor/ConversationMessageTimerUpdateEventProcessor.swift index afa3b1a6949..6e46afc521c 100644 --- a/WireDomain/Sources/WireDomain/Event Processing/ConversationEventProcessor/ConversationMessageTimerUpdateEventProcessor.swift +++ b/WireDomain/Sources/WireDomain/Event Processing/ConversationEventProcessor/ConversationMessageTimerUpdateEventProcessor.swift @@ -33,9 +33,8 @@ protocol ConversationMessageTimerUpdateEventProcessorProtocol { struct ConversationMessageTimerUpdateEventProcessor: ConversationMessageTimerUpdateEventProcessorProtocol { - let userRepository: any UserRepositoryProtocol let conversationLocalStore: any ConversationLocalStoreProtocol - let messageRepository: any MessageRepositoryProtocol + let messageLocalStore: any MessageLocalStoreProtocol func processEvent(_ event: ConversationMessageTimerUpdateEvent) async { let userID = event.senderID @@ -60,7 +59,7 @@ struct ConversationMessageTimerUpdateEventProcessor: ConversationMessageTimerUpd timeoutValue: timeoutValue ) - await messageRepository.addMessageToConversation( + await messageLocalStore.addSystemMessageToConversation( messageType: messageType, conversationID: conversationID.uuid, conversationDomain: conversationID.domain diff --git a/WireDomain/Tests/WireDomainTests/Event Processing/ConversationEventProcessor/ConversationMessageTimerUpdateEventProcessorTests.swift b/WireDomain/Tests/WireDomainTests/Event Processing/ConversationEventProcessor/ConversationMessageTimerUpdateEventProcessorTests.swift index 98def73b0d1..341363ade55 100644 --- a/WireDomain/Tests/WireDomainTests/Event Processing/ConversationEventProcessor/ConversationMessageTimerUpdateEventProcessorTests.swift +++ b/WireDomain/Tests/WireDomainTests/Event Processing/ConversationEventProcessor/ConversationMessageTimerUpdateEventProcessorTests.swift @@ -26,9 +26,8 @@ import XCTest final class ConversationMessageTimerUpdateEventProcessorTests: XCTestCase { private var sut: ConversationMessageTimerUpdateEventProcessor! - private var userRepository: MockUserRepositoryProtocol! private var conversationLocalStore: MockConversationLocalStoreProtocol! - private var messageRepository: MockMessageRepositoryProtocol! + private var messageLocalStore: MockMessageLocalStoreProtocol! private var coreDataStack: CoreDataStack! private var coreDataStackHelper: CoreDataStackHelper! private var modelHelper: ModelHelper! @@ -44,23 +43,20 @@ final class ConversationMessageTimerUpdateEventProcessorTests: XCTestCase { coreDataStackHelper = CoreDataStackHelper() coreDataStack = try await coreDataStackHelper.createStack() - userRepository = MockUserRepositoryProtocol() conversationLocalStore = MockConversationLocalStoreProtocol() - messageRepository = MockMessageRepositoryProtocol() + messageLocalStore = MockMessageLocalStoreProtocol() sut = ConversationMessageTimerUpdateEventProcessor( - userRepository: userRepository, conversationLocalStore: conversationLocalStore, - messageRepository: messageRepository + messageLocalStore: messageLocalStore ) } override func tearDown() async throws { try await super.tearDown() sut = nil - userRepository = nil conversationLocalStore = nil - messageRepository = nil + messageLocalStore = nil modelHelper = nil coreDataStack = nil try coreDataStackHelper.cleanupDirectory() @@ -79,8 +75,8 @@ final class ConversationMessageTimerUpdateEventProcessorTests: XCTestCase { conversationLocalStore.fetchOrCreateConversationIdDomain_MockValue = conversation conversationLocalStore.conversationMessageDestructionTimeout_MockValue = .fiveMinutes conversationLocalStore.storeConversationTimeoutValueFor_MockMethod = { _, _ in } - messageRepository - .addMessageToConversationMessageTypeConversationIDConversationDomain_MockMethod = { _, _, _ in } + messageLocalStore + .addSystemMessageToConversationMessageTypeConversationIDConversationDomain_MockMethod = { _, _, _ in } // When @@ -92,7 +88,7 @@ final class ConversationMessageTimerUpdateEventProcessorTests: XCTestCase { XCTAssertEqual(conversationLocalStore.conversationMessageDestructionTimeout_Invocations.count, 1) XCTAssertEqual(conversationLocalStore.storeConversationTimeoutValueFor_Invocations.count, 1) XCTAssertEqual( - messageRepository.addMessageToConversationMessageTypeConversationIDConversationDomain_Invocations.count, + messageLocalStore.addSystemMessageToConversationMessageTypeConversationIDConversationDomain_Invocations.count, 1 ) } From f7cb74e2c1b8166cd7eec8e0cc2687db51c2a473 Mon Sep 17 00:00:00 2001 From: Jullian Mercier <31648126+jullianm@users.noreply.github.com> Date: Tue, 3 Dec 2024 17:40:35 +0100 Subject: [PATCH 8/9] fix remaining conflicts --- .../LocalStores/ConversationLocalStoreTests.swift | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/WireDomain/Tests/WireDomainTests/LocalStores/ConversationLocalStoreTests.swift b/WireDomain/Tests/WireDomainTests/LocalStores/ConversationLocalStoreTests.swift index 8380109af11..319c46731fd 100644 --- a/WireDomain/Tests/WireDomainTests/LocalStores/ConversationLocalStoreTests.swift +++ b/WireDomain/Tests/WireDomainTests/LocalStores/ConversationLocalStoreTests.swift @@ -79,7 +79,7 @@ final class ConversationLocalStoreTests: XCTestCase { // When await sut.storeConversation( - groupConversation, + groupConversation.toDomainModel(), timestamp: .distantPast, isFederationEnabled: false, isMLSEnabled: true @@ -109,7 +109,8 @@ final class ConversationLocalStoreTests: XCTestCase { // When await sut.storeFailedConversation( - withQualifiedId: qualifiedID + conversationID: id, + conversationDomain: domain ) // Then @@ -142,7 +143,8 @@ final class ConversationLocalStoreTests: XCTestCase { await sut.storeConversation( needsBackendUpdate: true, - qualifiedId: qualifiedID + conversationID: id, + conversationDomain: domain ) // Then From fd7d30a993b5570e2798658eb2c6afb7c0e2b1ca Mon Sep 17 00:00:00 2001 From: Jullian Mercier <31648126+jullianm@users.noreply.github.com> Date: Tue, 3 Dec 2024 17:41:28 +0100 Subject: [PATCH 9/9] lint and format - WPB-10171 (#2098) --- .../Repositories/Message/MessageLocalStore.swift | 6 +++--- .../Repositories/Message/Models/MessageType.swift | 1 - ...tionMessageTimerUpdateEventProcessorTests.swift | 3 ++- .../Repositories/ConversationRepositoryTests.swift | 14 +++++++++++--- .../Source/Logging/LoggerProtocol.swift | 6 +++--- wire-ios-system/Source/Logging/WireLogger.swift | 2 +- 6 files changed, 20 insertions(+), 12 deletions(-) diff --git a/WireDomain/Sources/WireDomain/Repositories/Message/MessageLocalStore.swift b/WireDomain/Sources/WireDomain/Repositories/Message/MessageLocalStore.swift index 8465316e214..8c231ff56ef 100644 --- a/WireDomain/Sources/WireDomain/Repositories/Message/MessageLocalStore.swift +++ b/WireDomain/Sources/WireDomain/Repositories/Message/MessageLocalStore.swift @@ -344,14 +344,14 @@ public final class MessageLocalStore: MessageLocalStoreProtocol { return [systemMessage] case let .messageTimerUpdate(sender, date, timeoutValue): - + guard let sender = await fetchUser( id: sender.id, domain: sender.domain ) else { return [] } - + let systemMessage = await createSystemMessage( messageType: .messageTimerUpdate, sender: sender, @@ -359,7 +359,7 @@ public final class MessageLocalStore: MessageLocalStoreProtocol { timestamp: date, messageTimer: timeoutValue ) - + return [systemMessage] case let .conversationNameChanged(newName, sender, date): diff --git a/WireDomain/Sources/WireDomain/Repositories/Message/Models/MessageType.swift b/WireDomain/Sources/WireDomain/Repositories/Message/Models/MessageType.swift index 132f29d9ca9..ecb7c0ae782 100644 --- a/WireDomain/Sources/WireDomain/Repositories/Message/Models/MessageType.swift +++ b/WireDomain/Sources/WireDomain/Repositories/Message/Models/MessageType.swift @@ -81,7 +81,6 @@ public enum MessageType: Sendable { date: Date ) - case messageTimerUpdate( sender: (id: UUID, domain: String?), date: Date, diff --git a/WireDomain/Tests/WireDomainTests/Event Processing/ConversationEventProcessor/ConversationMessageTimerUpdateEventProcessorTests.swift b/WireDomain/Tests/WireDomainTests/Event Processing/ConversationEventProcessor/ConversationMessageTimerUpdateEventProcessorTests.swift index 341363ade55..1b96c363d68 100644 --- a/WireDomain/Tests/WireDomainTests/Event Processing/ConversationEventProcessor/ConversationMessageTimerUpdateEventProcessorTests.swift +++ b/WireDomain/Tests/WireDomainTests/Event Processing/ConversationEventProcessor/ConversationMessageTimerUpdateEventProcessorTests.swift @@ -88,7 +88,8 @@ final class ConversationMessageTimerUpdateEventProcessorTests: XCTestCase { XCTAssertEqual(conversationLocalStore.conversationMessageDestructionTimeout_Invocations.count, 1) XCTAssertEqual(conversationLocalStore.storeConversationTimeoutValueFor_Invocations.count, 1) XCTAssertEqual( - messageLocalStore.addSystemMessageToConversationMessageTypeConversationIDConversationDomain_Invocations.count, + messageLocalStore.addSystemMessageToConversationMessageTypeConversationIDConversationDomain_Invocations + .count, 1 ) } diff --git a/WireDomain/Tests/WireDomainTests/Repositories/ConversationRepositoryTests.swift b/WireDomain/Tests/WireDomainTests/Repositories/ConversationRepositoryTests.swift index 6a8873ccf74..b6e09b7e929 100644 --- a/WireDomain/Tests/WireDomainTests/Repositories/ConversationRepositoryTests.swift +++ b/WireDomain/Tests/WireDomainTests/Repositories/ConversationRepositoryTests.swift @@ -141,7 +141,8 @@ final class ConversationRepositoryTests: XCTestCase { failed: [] ) - conversationsLocalStore.storeConversationNeedsBackendUpdateConversationIDConversationDomain_MockMethod = { _, _, _ in } + conversationsLocalStore + .storeConversationNeedsBackendUpdateConversationIDConversationDomain_MockMethod = { _, _, _ in } // When @@ -151,7 +152,11 @@ final class ConversationRepositoryTests: XCTestCase { XCTAssertEqual(conversationsAPI.getLegacyConversationIdentifiers_Invocations.count, 1) XCTAssertEqual(conversationsAPI.getConversationsFor_Invocations.count, 1) - XCTAssertEqual(conversationsLocalStore.storeConversationNeedsBackendUpdateConversationIDConversationDomain_Invocations.count, 1) + XCTAssertEqual( + conversationsLocalStore.storeConversationNeedsBackendUpdateConversationIDConversationDomain_Invocations + .count, + 1 + ) } func testPullFailedConversations_It_Invokes_Local_Store_And_Conversation_API_Methods() async throws { @@ -181,7 +186,10 @@ final class ConversationRepositoryTests: XCTestCase { XCTAssertEqual(conversationsAPI.getLegacyConversationIdentifiers_Invocations.count, 1) XCTAssertEqual(conversationsAPI.getConversationsFor_Invocations.count, 1) - XCTAssertEqual(conversationsLocalStore.storeFailedConversationConversationIDConversationDomain_Invocations.count, 1) + XCTAssertEqual( + conversationsLocalStore.storeFailedConversationConversationIDConversationDomain_Invocations.count, + 1 + ) } func testPullMLSOneToOneConversation_It_Invokes_Local_Store_And_API_Methods() async throws { diff --git a/wire-ios-system/Source/Logging/LoggerProtocol.swift b/wire-ios-system/Source/Logging/LoggerProtocol.swift index 2a692fc5794..612e5476f40 100644 --- a/wire-ios-system/Source/Logging/LoggerProtocol.swift +++ b/wire-ios-system/Source/Logging/LoggerProtocol.swift @@ -31,9 +31,9 @@ public protocol LoggerProtocol { func addTag(_ key: LogAttributesKey, value: String?) } -extension LoggerProtocol { +public extension LoggerProtocol { - public func attributesDescription(from attributes: LogAttributes) -> String { + func attributesDescription(from attributes: LogAttributes) -> String { var logAttributes = attributes // drop attributes used for visibility and category @@ -55,7 +55,7 @@ extension LoggerProtocol { /// helper method to transform attributes array to single LogAttributes /// - note: if same key is contained accross multiple attributes, the latest one is taken - public func flattenArray(_ attributes: [LogAttributes]) -> LogAttributes { + func flattenArray(_ attributes: [LogAttributes]) -> LogAttributes { var mergedAttributes: LogAttributes = [:] attributes.forEach { mergedAttributes.merge($0) { _, new in new } diff --git a/wire-ios-system/Source/Logging/WireLogger.swift b/wire-ios-system/Source/Logging/WireLogger.swift index a8f4cd2f1de..a5d937ae7fb 100644 --- a/wire-ios-system/Source/Logging/WireLogger.swift +++ b/wire-ios-system/Source/Logging/WireLogger.swift @@ -27,7 +27,7 @@ public struct WireLogger: LoggerProtocol, Sendable { provider = AggregatedLogger(loggers: loggers) } - private static nonisolated(unsafe) var provider: (any LoggerProtocol)? + private nonisolated(unsafe) static var provider: (any LoggerProtocol)? public let tag: String