From ea1a2caa1329b24ed30845ee20440316120d5938 Mon Sep 17 00:00:00 2001 From: Nuno Vieira Date: Wed, 30 Oct 2024 13:44:37 +0000 Subject: [PATCH 01/10] Add support for creating system message in a channel --- .../Endpoints/Payloads/MessagePayloads.swift | 7 +++ .../ChannelController/ChannelController.swift | 49 ++++++++++++++++++- .../StreamChat/Database/DTOs/MessageDTO.swift | 13 +++++ .../StreamChat/Database/DatabaseSession.swift | 3 ++ .../StreamChatModel.xcdatamodel/contents | 1 + .../StreamChat/Workers/ChannelUpdater.swift | 7 +++ .../StreamChat/Workers/MessageUpdater.swift | 1 + 7 files changed, 80 insertions(+), 1 deletion(-) diff --git a/Sources/StreamChat/APIClient/Endpoints/Payloads/MessagePayloads.swift b/Sources/StreamChat/APIClient/Endpoints/Payloads/MessagePayloads.swift index 192f0dbee7e..cf21731af3b 100644 --- a/Sources/StreamChat/APIClient/Endpoints/Payloads/MessagePayloads.swift +++ b/Sources/StreamChat/APIClient/Endpoints/Payloads/MessagePayloads.swift @@ -252,6 +252,10 @@ struct MessageRequestBody: Encodable { let id: String let user: UserRequestBody let text: String + + // Used at the moment only for creating a system a message. + let type: String? + let command: String? let args: String? let parentId: String? @@ -269,6 +273,7 @@ struct MessageRequestBody: Encodable { id: String, user: UserRequestBody, text: String, + type: String?, command: String? = nil, args: String? = nil, parentId: String? = nil, @@ -285,6 +290,7 @@ struct MessageRequestBody: Encodable { self.id = id self.user = user self.text = text + self.type = type self.command = command self.args = args self.parentId = parentId @@ -312,6 +318,7 @@ struct MessageRequestBody: Encodable { try container.encodeIfPresent(pinExpires, forKey: .pinExpires) try container.encode(isSilent, forKey: .isSilent) try container.encodeIfPresent(pollId, forKey: .pollId) + try container.encodeIfPresent(type, forKey: .type) if !attachments.isEmpty { try container.encode(attachments, forKey: .attachments) diff --git a/Sources/StreamChat/Controllers/ChannelController/ChannelController.swift b/Sources/StreamChat/Controllers/ChannelController/ChannelController.swift index e572eceaa51..32eb439c60f 100644 --- a/Sources/StreamChat/Controllers/ChannelController/ChannelController.swift +++ b/Sources/StreamChat/Controllers/ChannelController/ChannelController.swift @@ -732,7 +732,53 @@ public class ChatChannelController: DataController, DelegateCallable, DataStoreP completion: completion ) } - + + /// Sends a system message to the channel. + /// + /// - Parameters: + /// - text: The text of the system message. + /// - messageId: The id for the sent message. By default, it is automatically generated by Stream. + /// - extraData: The extra data for the message. + /// - completion: Called when saving the message to the local DB finishes. + public func createSystemMessage( + text: String, + messageId: MessageId? = nil, + extraData: [String: RawJSON] = [:], + completion: ((Result) -> Void)? = nil + ) { + guard let cid = cid, isChannelAlreadyCreated else { + channelModificationFailed { error in + completion?(.failure(error ?? ClientError.Unknown())) + } + return + } + + updater.createNewMessage( + in: cid, + messageId: messageId, + text: text, + pinning: nil, + isSilent: false, + isSystem: true, + command: nil, + arguments: nil, + attachments: [], + mentionedUserIds: [], + quotedMessageId: nil, + skipPush: false, + skipEnrichUrl: false, + poll: nil, + extraData: extraData + ) { result in + if let newMessage = try? result.get() { + self.client.eventNotificationCenter.process(NewMessagePendingEvent(message: newMessage)) + } + self.callback { + completion?(result.map(\.id)) + } + } + } + /// Creates a new poll. /// /// - Parameters: @@ -1285,6 +1331,7 @@ public class ChatChannelController: DataController, DelegateCallable, DataStoreP text: text, pinning: pinning, isSilent: isSilent, + isSystem: false, command: nil, arguments: nil, attachments: attachments, diff --git a/Sources/StreamChat/Database/DTOs/MessageDTO.swift b/Sources/StreamChat/Database/DTOs/MessageDTO.swift index a18e502ccc9..d93393ba6d8 100644 --- a/Sources/StreamChat/Database/DTOs/MessageDTO.swift +++ b/Sources/StreamChat/Database/DTOs/MessageDTO.swift @@ -38,6 +38,10 @@ class MessageDTO: NSManagedObject { @NSManaged var replyCount: Int32 @NSManaged var extraData: Data? @NSManaged var isSilent: Bool + + // Used for creating a message as a system message from the client SDK. + @NSManaged var isSystem: Bool + @NSManaged var skipPush: Bool @NSManaged var skipEnrichUrl: Bool @NSManaged var isShadowed: Bool @@ -621,6 +625,7 @@ extension NSManagedObjectContext: MessageDatabaseSession { mentionedUserIds: [UserId], showReplyInChannel: Bool, isSilent: Bool, + isSystem: Bool, quotedMessageId: MessageId?, createdAt: Date?, skipPush: Bool, @@ -661,6 +666,10 @@ extension NSManagedObjectContext: MessageDatabaseSession { message.parentMessageId = parentMessageId message.extraData = try JSONEncoder.default.encode(extraData) message.isSilent = isSilent + message.isSystem = isSystem + if isSystem { + message.type = MessageType.system.rawValue + } message.skipPush = skipPush message.skipEnrichUrl = skipEnrichUrl message.reactionScores = [:] @@ -1245,10 +1254,14 @@ extension MessageDTO { .sorted { ($0.attachmentID?.index ?? 0) < ($1.attachmentID?.index ?? 0) } .compactMap { $0.asRequestPayload() } + // At the moment, we only provide the type for system messages when creating a message. + let systemType = isSystem ? MessageType.system.rawValue : nil + return .init( id: id, user: user.asRequestBody(), text: text, + type: systemType, command: command, args: args, parentId: parentMessageId, diff --git a/Sources/StreamChat/Database/DatabaseSession.swift b/Sources/StreamChat/Database/DatabaseSession.swift index 517e82194ef..2fe40651def 100644 --- a/Sources/StreamChat/Database/DatabaseSession.swift +++ b/Sources/StreamChat/Database/DatabaseSession.swift @@ -79,6 +79,7 @@ protocol MessageDatabaseSession { mentionedUserIds: [UserId], showReplyInChannel: Bool, isSilent: Bool, + isSystem: Bool, quotedMessageId: MessageId?, createdAt: Date?, skipPush: Bool, @@ -220,6 +221,7 @@ extension MessageDatabaseSession { pinning: MessagePinning?, quotedMessageId: MessageId?, isSilent: Bool = false, + isSystem: Bool, skipPush: Bool, skipEnrichUrl: Bool, attachments: [AnyAttachmentPayload] = [], @@ -239,6 +241,7 @@ extension MessageDatabaseSession { mentionedUserIds: mentionedUserIds, showReplyInChannel: false, isSilent: isSilent, + isSystem: isSystem, quotedMessageId: quotedMessageId, createdAt: nil, skipPush: skipPush, diff --git a/Sources/StreamChat/Database/StreamChatModel.xcdatamodeld/StreamChatModel.xcdatamodel/contents b/Sources/StreamChat/Database/StreamChatModel.xcdatamodeld/StreamChatModel.xcdatamodel/contents index aabbf47721f..ac1e575aab3 100644 --- a/Sources/StreamChat/Database/StreamChatModel.xcdatamodeld/StreamChatModel.xcdatamodel/contents +++ b/Sources/StreamChat/Database/StreamChatModel.xcdatamodeld/StreamChatModel.xcdatamodel/contents @@ -208,6 +208,7 @@ + diff --git a/Sources/StreamChat/Workers/ChannelUpdater.swift b/Sources/StreamChat/Workers/ChannelUpdater.swift index 9e1da48718a..e8876f7f110 100644 --- a/Sources/StreamChat/Workers/ChannelUpdater.swift +++ b/Sources/StreamChat/Workers/ChannelUpdater.swift @@ -202,6 +202,7 @@ class ChannelUpdater: Worker { id: .newUniqueId, user: user, text: message, + type: nil, command: nil, args: nil, parentId: nil, @@ -285,6 +286,7 @@ class ChannelUpdater: Worker { /// - text: Text of the message. /// - pinning: Pins the new message. Nil if should not be pinned. /// - isSilent: A flag indicating whether the message is a silent message. Silent messages are special messages that don't increase the unread messages count nor mark a channel as unread. + /// - isSystem: A flag indicating whether the message is a system message. /// - attachments: An array of the attachments for the message. /// - quotedMessageId: An id of the message new message quotes. (inline reply) /// - skipPush: If true, skips sending push notification to channel members. @@ -298,6 +300,7 @@ class ChannelUpdater: Worker { text: String, pinning: MessagePinning? = nil, isSilent: Bool, + isSystem: Bool, command: String?, arguments: String?, attachments: [AnyAttachmentPayload] = [], @@ -323,6 +326,7 @@ class ChannelUpdater: Worker { mentionedUserIds: mentionedUserIds, showReplyInChannel: false, isSilent: isSilent, + isSystem: isSystem, quotedMessageId: quotedMessageId, createdAt: nil, skipPush: skipPush, @@ -673,6 +677,7 @@ class ChannelUpdater: Worker { id: .newUniqueId, user: userRequestBody, text: text, + type: nil, extraData: [:] ) return messagePayload @@ -730,6 +735,7 @@ extension ChannelUpdater { text: String, pinning: MessagePinning? = nil, isSilent: Bool, + isSystem: Bool, command: String?, arguments: String?, attachments: [AnyAttachmentPayload] = [], @@ -746,6 +752,7 @@ extension ChannelUpdater { text: text, pinning: pinning, isSilent: isSilent, + isSystem: isSystem, command: command, arguments: arguments, attachments: attachments, diff --git a/Sources/StreamChat/Workers/MessageUpdater.swift b/Sources/StreamChat/Workers/MessageUpdater.swift index 84821e94c69..ee6f045c266 100644 --- a/Sources/StreamChat/Workers/MessageUpdater.swift +++ b/Sources/StreamChat/Workers/MessageUpdater.swift @@ -230,6 +230,7 @@ class MessageUpdater: Worker { mentionedUserIds: mentionedUserIds, showReplyInChannel: showReplyInChannel, isSilent: isSilent, + isSystem: false, quotedMessageId: quotedMessageId, createdAt: nil, skipPush: skipPush, From 1541b8a979abd748ae3a595ee9342eeb55aeec58 Mon Sep 17 00:00:00 2001 From: Nuno Vieira Date: Wed, 30 Oct 2024 13:45:08 +0000 Subject: [PATCH 02/10] Add support for creating system message in the State Layer --- Sources/StreamChat/StateLayer/Chat.swift | 36 +++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/Sources/StreamChat/StateLayer/Chat.swift b/Sources/StreamChat/StateLayer/Chat.swift index 4e4b939bb13..86019a77589 100644 --- a/Sources/StreamChat/StateLayer/Chat.swift +++ b/Sources/StreamChat/StateLayer/Chat.swift @@ -457,6 +457,7 @@ public class Chat { text: text, pinning: pinning, isSilent: silent, + isSystem: false, command: nil, arguments: nil, attachments: attachments, @@ -471,7 +472,40 @@ public class Chat { eventNotificationCenter.process(NewMessagePendingEvent(message: localMessage)) return try await sentMessage } - + + /// Sends a system message to the channel. + /// + /// - Parameters: + /// - text: Text of the message. + /// - messageId: A custom id for the sent message. By default, it is automatically generated by Stream. + /// - extraData: Additional extra data of the message object. + @discardableResult + public func sendSystemMessage( + text: String, + messageId: MessageId? = nil, + extraData: [String: RawJSON] = [:] + ) async throws -> ChatMessage { + let localMessage = try await channelUpdater.createNewMessage( + in: cid, + messageId: messageId, + text: text, + pinning: nil, + isSilent: false, + isSystem: true, + command: nil, + arguments: nil, + attachments: [], + mentionedUserIds: [], + quotedMessageId: nil, + skipPush: false, + skipEnrichUrl: false, + extraData: extraData + ) + // Important to set up the waiter immediately + async let sentMessage = try await waitForAPIRequest(localMessage: localMessage) + return try await sentMessage + } + /// Edits the specified message in the channel. /// /// - Parameters: From e75e34af5243e6b4a9c64e3fba44f0861602d849 Mon Sep 17 00:00:00 2001 From: Nuno Vieira Date: Wed, 30 Oct 2024 13:46:27 +0000 Subject: [PATCH 03/10] Add option to create system messages in the Demo App --- .../Components/DemoChatChannelListRouter.swift | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/DemoApp/StreamChat/Components/DemoChatChannelListRouter.swift b/DemoApp/StreamChat/Components/DemoChatChannelListRouter.swift index 39455f3bede..e91d805373d 100644 --- a/DemoApp/StreamChat/Components/DemoChatChannelListRouter.swift +++ b/DemoApp/StreamChat/Components/DemoChatChannelListRouter.swift @@ -480,6 +480,15 @@ final class DemoChatChannelListRouter: ChatChannelListRouter { channelController.createNewMessage(text: message, skipPush: true) } }), + .init(title: "Send system message", isEnabled: canSendMessage, handler: { [unowned self] _ in + self.rootViewController.presentAlert(title: "Enter the message text", textFieldPlaceholder: "Send message") { message in + guard let message = message, !message.isEmpty else { + self.rootViewController.presentAlert(title: "Message is not valid") + return + } + channelController.createSystemMessage(text: message) + } + }), .init(title: "Send message without url enriching", isEnabled: canSendMessage, handler: { [unowned self] _ in self.rootViewController.presentAlert(title: "Enter the message text", textFieldPlaceholder: "Send message") { message in guard let message = message, !message.isEmpty else { From 13bf49fcf2617a3b5494f673ebbc2b00f62e3f2d Mon Sep 17 00:00:00 2001 From: Nuno Vieira Date: Wed, 30 Oct 2024 13:47:44 +0000 Subject: [PATCH 04/10] Make sure local system message does not update channel.lastMessageAt if skipping is enabled --- Sources/StreamChat/Database/DTOs/MessageDTO.swift | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Sources/StreamChat/Database/DTOs/MessageDTO.swift b/Sources/StreamChat/Database/DTOs/MessageDTO.swift index d93393ba6d8..cccee654a26 100644 --- a/Sources/StreamChat/Database/DTOs/MessageDTO.swift +++ b/Sources/StreamChat/Database/DTOs/MessageDTO.swift @@ -695,9 +695,12 @@ extension NSManagedObjectContext: MessageDatabaseSession { message.user = currentUserDTO.user message.channel = channelDTO - let newLastMessageAt = max(channelDTO.lastMessageAt?.bridgeDate ?? createdAt, createdAt).bridgeDate - channelDTO.lastMessageAt = newLastMessageAt - channelDTO.defaultSortingAt = newLastMessageAt + let shouldNotUpdateLastMessageAt = isSystem && channelDTO.config.skipLastMsgAtUpdateForSystemMsg + if !shouldNotUpdateLastMessageAt { + let newLastMessageAt = max(channelDTO.lastMessageAt?.bridgeDate ?? createdAt, createdAt).bridgeDate + channelDTO.lastMessageAt = newLastMessageAt + channelDTO.defaultSortingAt = newLastMessageAt + } if let parentMessageId = parentMessageId, let parentMessageDTO = MessageDTO.load(id: parentMessageId, context: self) { From 9098a61c67a68d5cb9ebf9d944652765899c83b3 Mon Sep 17 00:00:00 2001 From: Nuno Vieira Date: Wed, 30 Oct 2024 14:04:16 +0000 Subject: [PATCH 05/10] Fix compilation of tests --- .../Endpoints/Payloads/MessagePayloads.swift | 2 +- .../StreamChat/Database/DatabaseSession_Mock.swift | 2 ++ .../StreamChat/Workers/ChannelUpdater_Mock.swift | 3 +++ .../Endpoints/ChannelEndpoints_Tests.swift | 1 + .../Endpoints/MessageEndpoints_Tests.swift | 1 + .../Endpoints/Payloads/MessagePayloads_Tests.swift | 2 ++ .../ChannelController_Tests.swift | 1 + .../Database/DTOs/AttachmentDTO_Tests.swift | 1 + .../Database/DTOs/MessageDTO_Tests.swift | 11 +++++++++++ .../DTOs/NSManagedObject+Validation_Tests.swift | 1 + .../Repositories/MessageRepository_Tests.swift | 1 + .../OfflineRequestsRepository_Tests.swift | 8 +++++++- .../Workers/Background/MessageSender_Tests.swift | 14 ++++++++++++++ .../Workers/ChannelUpdater_Tests.swift | 6 ++++++ 14 files changed, 52 insertions(+), 2 deletions(-) diff --git a/Sources/StreamChat/APIClient/Endpoints/Payloads/MessagePayloads.swift b/Sources/StreamChat/APIClient/Endpoints/Payloads/MessagePayloads.swift index cf21731af3b..7f94aafc262 100644 --- a/Sources/StreamChat/APIClient/Endpoints/Payloads/MessagePayloads.swift +++ b/Sources/StreamChat/APIClient/Endpoints/Payloads/MessagePayloads.swift @@ -273,7 +273,7 @@ struct MessageRequestBody: Encodable { id: String, user: UserRequestBody, text: String, - type: String?, + type: String? = nil, command: String? = nil, args: String? = nil, parentId: String? = nil, diff --git a/TestTools/StreamChatTestTools/Mocks/StreamChat/Database/DatabaseSession_Mock.swift b/TestTools/StreamChatTestTools/Mocks/StreamChat/Database/DatabaseSession_Mock.swift index e2909c43e4a..cfe11f155b2 100644 --- a/TestTools/StreamChatTestTools/Mocks/StreamChat/Database/DatabaseSession_Mock.swift +++ b/TestTools/StreamChatTestTools/Mocks/StreamChat/Database/DatabaseSession_Mock.swift @@ -134,6 +134,7 @@ class DatabaseSession_Mock: DatabaseSession { mentionedUserIds: [UserId], showReplyInChannel: Bool, isSilent: Bool, + isSystem: Bool, quotedMessageId: MessageId?, createdAt: Date?, skipPush: Bool, @@ -155,6 +156,7 @@ class DatabaseSession_Mock: DatabaseSession { mentionedUserIds: mentionedUserIds, showReplyInChannel: showReplyInChannel, isSilent: isSilent, + isSystem: isSystem, quotedMessageId: quotedMessageId, createdAt: createdAt, skipPush: skipPush, diff --git a/TestTools/StreamChatTestTools/Mocks/StreamChat/Workers/ChannelUpdater_Mock.swift b/TestTools/StreamChatTestTools/Mocks/StreamChat/Workers/ChannelUpdater_Mock.swift index 047d9d5efa7..5e589373f84 100644 --- a/TestTools/StreamChatTestTools/Mocks/StreamChat/Workers/ChannelUpdater_Mock.swift +++ b/TestTools/StreamChatTestTools/Mocks/StreamChat/Workers/ChannelUpdater_Mock.swift @@ -77,6 +77,7 @@ final class ChannelUpdater_Mock: ChannelUpdater { @Atomic var createNewMessage_cid: ChannelId? @Atomic var createNewMessage_text: String? @Atomic var createNewMessage_isSilent: Bool? + @Atomic var createNewMessage_isSystem: Bool? @Atomic var createNewMessage_skipPush: Bool? @Atomic var createNewMessage_skipEnrichUrl: Bool? @Atomic var createNewMessage_command: String? @@ -343,6 +344,7 @@ final class ChannelUpdater_Mock: ChannelUpdater { text: String, pinning: MessagePinning?, isSilent: Bool, + isSystem: Bool, command: String?, arguments: String?, attachments: [AnyAttachmentPayload], @@ -357,6 +359,7 @@ final class ChannelUpdater_Mock: ChannelUpdater { createNewMessage_cid = cid createNewMessage_text = text createNewMessage_isSilent = isSilent + createNewMessage_isSystem = isSystem createNewMessage_skipPush = skipPush createNewMessage_skipEnrichUrl = skipEnrichUrl createNewMessage_command = command diff --git a/Tests/StreamChatTests/APIClient/Endpoints/ChannelEndpoints_Tests.swift b/Tests/StreamChatTests/APIClient/Endpoints/ChannelEndpoints_Tests.swift index 6a0c58e1e7b..422d5c74c0a 100644 --- a/Tests/StreamChatTests/APIClient/Endpoints/ChannelEndpoints_Tests.swift +++ b/Tests/StreamChatTests/APIClient/Endpoints/ChannelEndpoints_Tests.swift @@ -268,6 +268,7 @@ final class ChannelEndpoints_Tests: XCTestCase { id: .unique, user: .dummy(userId: .unique), text: .unique, + type: nil, command: .unique, args: .unique, parentId: .unique, diff --git a/Tests/StreamChatTests/APIClient/Endpoints/MessageEndpoints_Tests.swift b/Tests/StreamChatTests/APIClient/Endpoints/MessageEndpoints_Tests.swift index 23e238d7a83..53a9b9123f2 100644 --- a/Tests/StreamChatTests/APIClient/Endpoints/MessageEndpoints_Tests.swift +++ b/Tests/StreamChatTests/APIClient/Endpoints/MessageEndpoints_Tests.swift @@ -73,6 +73,7 @@ final class MessageEndpoints_Tests: XCTestCase { id: .unique, user: .init(id: .unique, name: .unique, imageURL: .unique(), extraData: .init()), text: .unique, + type: nil, extraData: [:] ) diff --git a/Tests/StreamChatTests/APIClient/Endpoints/Payloads/MessagePayloads_Tests.swift b/Tests/StreamChatTests/APIClient/Endpoints/Payloads/MessagePayloads_Tests.swift index 443477c8fb8..bf05471e16d 100644 --- a/Tests/StreamChatTests/APIClient/Endpoints/Payloads/MessagePayloads_Tests.swift +++ b/Tests/StreamChatTests/APIClient/Endpoints/Payloads/MessagePayloads_Tests.swift @@ -138,6 +138,7 @@ final class MessageRequestBody_Tests: XCTestCase { id: .unique, user: .dummy(userId: .unique), text: .unique, + type: nil, command: .unique, args: .unique, parentId: .unique, @@ -175,6 +176,7 @@ final class MessageRequestBody_Tests: XCTestCase { id: .unique, user: .dummy(userId: .unique), text: .unique, + type: nil, command: .unique, args: .unique, parentId: .unique, diff --git a/Tests/StreamChatTests/Controllers/ChannelController/ChannelController_Tests.swift b/Tests/StreamChatTests/Controllers/ChannelController/ChannelController_Tests.swift index 3596b200b3f..21025f8b58b 100644 --- a/Tests/StreamChatTests/Controllers/ChannelController/ChannelController_Tests.swift +++ b/Tests/StreamChatTests/Controllers/ChannelController/ChannelController_Tests.swift @@ -943,6 +943,7 @@ final class ChannelController_Tests: XCTestCase { mentionedUserIds: [], showReplyInChannel: false, isSilent: false, + isSystem: false, quotedMessageId: nil, createdAt: oldMessageCreatedAt, skipPush: false, diff --git a/Tests/StreamChatTests/Database/DTOs/AttachmentDTO_Tests.swift b/Tests/StreamChatTests/Database/DTOs/AttachmentDTO_Tests.swift index d2a803e5711..40987e368d2 100644 --- a/Tests/StreamChatTests/Database/DTOs/AttachmentDTO_Tests.swift +++ b/Tests/StreamChatTests/Database/DTOs/AttachmentDTO_Tests.swift @@ -295,6 +295,7 @@ final class AttachmentDTO_Tests: XCTestCase { pinning: nil, quotedMessageId: nil, isSilent: false, + isSystem: false, skipPush: false, skipEnrichUrl: false, attachments: [.init(payload: TestAttachmentPayload.unique)], diff --git a/Tests/StreamChatTests/Database/DTOs/MessageDTO_Tests.swift b/Tests/StreamChatTests/Database/DTOs/MessageDTO_Tests.swift index 438b9ec25bd..ba9489668b7 100644 --- a/Tests/StreamChatTests/Database/DTOs/MessageDTO_Tests.swift +++ b/Tests/StreamChatTests/Database/DTOs/MessageDTO_Tests.swift @@ -1190,6 +1190,7 @@ final class MessageDTO_Tests: XCTestCase { mentionedUserIds: mentionedUserIds, showReplyInChannel: messageShowReplyInChannel, isSilent: messageIsSilent, + isSystem: false, quotedMessageId: nil, createdAt: nil, skipPush: false, @@ -1332,6 +1333,7 @@ final class MessageDTO_Tests: XCTestCase { mentionedUserIds: [], showReplyInChannel: false, isSilent: false, + isSystem: false, quotedMessageId: nil, createdAt: nil, skipPush: false, @@ -1355,6 +1357,7 @@ final class MessageDTO_Tests: XCTestCase { mentionedUserIds: [], showReplyInChannel: false, isSilent: false, + isSystem: false, quotedMessageId: nil, createdAt: nil, skipPush: false, @@ -1497,6 +1500,7 @@ final class MessageDTO_Tests: XCTestCase { mentionedUserIds: newMentionedUserIds, showReplyInChannel: true, isSilent: false, + isSystem: false, quotedMessageId: nil, createdAt: nil, skipPush: true, @@ -1564,6 +1568,7 @@ final class MessageDTO_Tests: XCTestCase { mentionedUserIds: [], showReplyInChannel: false, isSilent: false, + isSystem: false, quotedMessageId: nil, createdAt: nil, skipPush: false, @@ -1610,6 +1615,7 @@ final class MessageDTO_Tests: XCTestCase { mentionedUserIds: [], showReplyInChannel: true, isSilent: false, + isSystem: false, quotedMessageId: nil, createdAt: nil, skipPush: false, @@ -1669,6 +1675,7 @@ final class MessageDTO_Tests: XCTestCase { mentionedUserIds: [], showReplyInChannel: false, isSilent: false, + isSystem: false, quotedMessageId: nil, createdAt: nil, skipPush: false, @@ -1697,6 +1704,7 @@ final class MessageDTO_Tests: XCTestCase { mentionedUserIds: [.unique], showReplyInChannel: true, isSilent: false, + isSystem: false, quotedMessageId: nil, createdAt: nil, skipPush: false, @@ -1739,6 +1747,7 @@ final class MessageDTO_Tests: XCTestCase { mentionedUserIds: [.unique], showReplyInChannel: true, isSilent: false, + isSystem: false, quotedMessageId: nil, createdAt: nil, skipPush: false, @@ -1781,6 +1790,7 @@ final class MessageDTO_Tests: XCTestCase { pinning: MessagePinning(expirationDate: .unique), quotedMessageId: nil, isSilent: false, + isSystem: false, skipPush: false, skipEnrichUrl: false, extraData: [:] @@ -1826,6 +1836,7 @@ final class MessageDTO_Tests: XCTestCase { mentionedUserIds: [], showReplyInChannel: false, isSilent: false, + isSystem: false, quotedMessageId: nil, createdAt: nil, skipPush: false, diff --git a/Tests/StreamChatTests/Database/DTOs/NSManagedObject+Validation_Tests.swift b/Tests/StreamChatTests/Database/DTOs/NSManagedObject+Validation_Tests.swift index 8bdc0cda6f1..64c68bbe6a4 100644 --- a/Tests/StreamChatTests/Database/DTOs/NSManagedObject+Validation_Tests.swift +++ b/Tests/StreamChatTests/Database/DTOs/NSManagedObject+Validation_Tests.swift @@ -80,6 +80,7 @@ private extension NSManagedObject_Validation_Tests { text: "Hello", pinning: nil, quotedMessageId: nil, + isSystem: false, skipPush: false, skipEnrichUrl: false ) diff --git a/Tests/StreamChatTests/Repositories/MessageRepository_Tests.swift b/Tests/StreamChatTests/Repositories/MessageRepository_Tests.swift index d845a88eecd..653e8244901 100644 --- a/Tests/StreamChatTests/Repositories/MessageRepository_Tests.swift +++ b/Tests/StreamChatTests/Repositories/MessageRepository_Tests.swift @@ -674,6 +674,7 @@ extension MessageRepositoryTests { pinning: nil, quotedMessageId: nil, isSilent: false, + isSystem: false, skipPush: skipPush, skipEnrichUrl: skipEnrichUrl, extraData: [:] diff --git a/Tests/StreamChatTests/Repositories/OfflineRequestsRepository_Tests.swift b/Tests/StreamChatTests/Repositories/OfflineRequestsRepository_Tests.swift index deb7085a411..b82a419a6fc 100644 --- a/Tests/StreamChatTests/Repositories/OfflineRequestsRepository_Tests.swift +++ b/Tests/StreamChatTests/Repositories/OfflineRequestsRepository_Tests.swift @@ -331,7 +331,13 @@ final class OfflineRequestsRepository_Tests: XCTestCase { private func createSendMessageRequest(requestIdNumber: Int, messageIdNumber: Int, date: Date) throws { let id = "request\(requestIdNumber)" let messageId = "message\(messageIdNumber)" - let requestBody = MessageRequestBody(id: messageId, user: .dummy(userId: .unique), text: .unique, extraData: [:]) + let requestBody = MessageRequestBody( + id: messageId, + user: .dummy(userId: .unique), + text: .unique, + type: nil, + extraData: [:] + ) let endpoint: Endpoint = .sendMessage( cid: .init(type: .messaging, id: id), messagePayload: requestBody, diff --git a/Tests/StreamChatTests/Workers/Background/MessageSender_Tests.swift b/Tests/StreamChatTests/Workers/Background/MessageSender_Tests.swift index ff5bb560111..bb2872f1fce 100644 --- a/Tests/StreamChatTests/Workers/Background/MessageSender_Tests.swift +++ b/Tests/StreamChatTests/Workers/Background/MessageSender_Tests.swift @@ -80,6 +80,7 @@ final class MessageSender_Tests: XCTestCase { pinning: nil, quotedMessageId: nil, isSilent: false, + isSystem: false, skipPush: false, skipEnrichUrl: false, extraData: [:] @@ -94,6 +95,7 @@ final class MessageSender_Tests: XCTestCase { pinning: nil, quotedMessageId: nil, isSilent: false, + isSystem: false, skipPush: false, skipEnrichUrl: false, attachments: [ @@ -114,6 +116,7 @@ final class MessageSender_Tests: XCTestCase { pinning: nil, quotedMessageId: nil, isSilent: false, + isSystem: false, skipPush: false, skipEnrichUrl: false, attachments: [], @@ -157,6 +160,7 @@ final class MessageSender_Tests: XCTestCase { pinning: nil, quotedMessageId: nil, isSilent: false, + isSystem: false, skipPush: false, skipEnrichUrl: false, attachments: [ @@ -184,6 +188,7 @@ final class MessageSender_Tests: XCTestCase { pinning: nil, quotedMessageId: nil, isSilent: false, + isSystem: false, skipPush: false, skipEnrichUrl: false, attachments: [ @@ -224,6 +229,7 @@ final class MessageSender_Tests: XCTestCase { pinning: nil, quotedMessageId: nil, isSilent: false, + isSystem: false, skipPush: false, skipEnrichUrl: false, extraData: [:] @@ -238,6 +244,7 @@ final class MessageSender_Tests: XCTestCase { pinning: nil, quotedMessageId: nil, isSilent: false, + isSystem: false, skipPush: false, skipEnrichUrl: false, extraData: [:] @@ -252,6 +259,7 @@ final class MessageSender_Tests: XCTestCase { pinning: nil, quotedMessageId: nil, isSilent: false, + isSystem: false, skipPush: false, skipEnrichUrl: false, extraData: [:] @@ -301,6 +309,7 @@ final class MessageSender_Tests: XCTestCase { pinning: nil, quotedMessageId: nil, isSilent: false, + isSystem: false, skipPush: false, skipEnrichUrl: false, extraData: [:] @@ -315,6 +324,7 @@ final class MessageSender_Tests: XCTestCase { pinning: nil, quotedMessageId: nil, isSilent: false, + isSystem: false, skipPush: false, skipEnrichUrl: false, extraData: [:] @@ -329,6 +339,7 @@ final class MessageSender_Tests: XCTestCase { pinning: nil, quotedMessageId: nil, isSilent: false, + isSystem: false, skipPush: false, skipEnrichUrl: false, extraData: [:] @@ -343,6 +354,7 @@ final class MessageSender_Tests: XCTestCase { pinning: nil, quotedMessageId: nil, isSilent: false, + isSystem: false, skipPush: false, skipEnrichUrl: false, extraData: [:] @@ -388,6 +400,7 @@ final class MessageSender_Tests: XCTestCase { pinning: nil, quotedMessageId: nil, isSilent: false, + isSystem: false, skipPush: false, skipEnrichUrl: false, attachments: [ @@ -516,6 +529,7 @@ final class MessageSender_Tests: XCTestCase { text: "\(id)", pinning: nil, quotedMessageId: nil, + isSystem: false, skipPush: false, skipEnrichUrl: false ) diff --git a/Tests/StreamChatTests/Workers/ChannelUpdater_Tests.swift b/Tests/StreamChatTests/Workers/ChannelUpdater_Tests.swift index a66892663a0..fcd64a7532e 100644 --- a/Tests/StreamChatTests/Workers/ChannelUpdater_Tests.swift +++ b/Tests/StreamChatTests/Workers/ChannelUpdater_Tests.swift @@ -577,6 +577,7 @@ final class ChannelUpdater_Tests: XCTestCase { text: text, pinning: MessagePinning(expirationDate: .unique), isSilent: false, + isSystem: false, command: command, arguments: arguments, attachments: attachmentEnvelopes, @@ -608,6 +609,7 @@ final class ChannelUpdater_Tests: XCTestCase { XCTAssertEqual(messageDTO.skipPush, true) XCTAssertEqual(messageDTO.skipEnrichUrl, true) XCTAssertEqual(messageDTO.mentionedUserIds, [currentUserId]) + XCTAssertEqual(messageDTO.isSystem, false) let message = try messageDTO.asModel() XCTAssertEqual(message.text, text) @@ -657,6 +659,7 @@ final class ChannelUpdater_Tests: XCTestCase { messageId: .unique, text: .unique, isSilent: false, + isSystem: false, command: .unique, arguments: .unique, mentionedUserIds: [.unique], @@ -1004,6 +1007,7 @@ final class ChannelUpdater_Tests: XCTestCase { .message?.id ?? "id", user: currentUser, text: systemMessage, + type: nil, extraData: [:] ) ))) @@ -1227,6 +1231,7 @@ final class ChannelUpdater_Tests: XCTestCase { id: messageId, user: UserRequestBody(id: senderId, name: nil, imageURL: nil, extraData: [:]), text: message, + type: nil, extraData: [:] ) let referenceEndpoint: Endpoint = .addMembers( @@ -1456,6 +1461,7 @@ final class ChannelUpdater_Tests: XCTestCase { id: messageId, user: UserRequestBody(id: senderId, name: nil, imageURL: nil, extraData: [:]), text: message, + type: nil, extraData: [:] ) let referenceEndpoint: Endpoint = .removeMembers( From 3e81f9b52236fa53b679ef682d68120ea6d50bc8 Mon Sep 17 00:00:00 2001 From: Nuno Vieira Date: Wed, 30 Oct 2024 14:43:07 +0000 Subject: [PATCH 06/10] Add test coverage to State Layer --- Sources/StreamChat/StateLayer/Chat.swift | 3 ++- .../StateLayer/Chat_Tests.swift | 21 ++++++++++++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/Sources/StreamChat/StateLayer/Chat.swift b/Sources/StreamChat/StateLayer/Chat.swift index 86019a77589..042787ce99e 100644 --- a/Sources/StreamChat/StateLayer/Chat.swift +++ b/Sources/StreamChat/StateLayer/Chat.swift @@ -481,7 +481,7 @@ public class Chat { /// - extraData: Additional extra data of the message object. @discardableResult public func sendSystemMessage( - text: String, + with text: String, messageId: MessageId? = nil, extraData: [String: RawJSON] = [:] ) async throws -> ChatMessage { @@ -503,6 +503,7 @@ public class Chat { ) // Important to set up the waiter immediately async let sentMessage = try await waitForAPIRequest(localMessage: localMessage) + eventNotificationCenter.process(NewMessagePendingEvent(message: localMessage)) return try await sentMessage } diff --git a/Tests/StreamChatTests/StateLayer/Chat_Tests.swift b/Tests/StreamChatTests/StateLayer/Chat_Tests.swift index 5859d0bee02..768511f8ede 100644 --- a/Tests/StreamChatTests/StateLayer/Chat_Tests.swift +++ b/Tests/StreamChatTests/StateLayer/Chat_Tests.swift @@ -636,7 +636,26 @@ final class Chat_Tests: XCTestCase { XCTAssertEqual(text, stateMessage.text) XCTAssertEqual(LocalMessageState.sendingFailed, stateMessage.localState) } - + + func test_sendSystemMessage_thenCallsChannelUpdaterWithIsSystemTrue() async throws { + try await setUpChat(usesMockedUpdaters: true) + await XCTAssertEqual(0, chat.state.messages.count) + + let text = "Text" + env.channelUpdaterMock.createNewMessage_completion_result = .success(( + .mock(id: "0", text: text, type: .system) + )) + let message = try await chat.sendSystemMessage( + with: text, + messageId: "0" + ) + + XCTAssertEqual(env.typingEventsSenderMock.stopTyping_cid, nil) + XCTAssertEqual(env.channelUpdaterMock.createNewMessage_isSystem, true) + XCTAssertEqual(text, message.text) + XCTAssertEqual(.system, message.type) + } + func test_updateMessage_whenAPIRequestSucceeds_thenUpdateMessageSucceeds() async throws { try await env.client.databaseContainer.write { session in try session.saveChannel(payload: self.makeChannelPayload(messageCount: 1, createdAtOffset: 0)) From 6a10e90fe8fe1378f5f19675e69262488dde757c Mon Sep 17 00:00:00 2001 From: Nuno Vieira Date: Wed, 30 Oct 2024 15:32:04 +0000 Subject: [PATCH 07/10] Add test coverage when creating a system message --- .../Payloads/MessagePayloads_Tests.swift | 22 ++++ .../ChannelController_Tests.swift | 29 +++++ .../Database/DTOs/MessageDTO_Tests.swift | 108 +++++++++++++++++- 3 files changed, 158 insertions(+), 1 deletion(-) diff --git a/Tests/StreamChatTests/APIClient/Endpoints/Payloads/MessagePayloads_Tests.swift b/Tests/StreamChatTests/APIClient/Endpoints/Payloads/MessagePayloads_Tests.swift index bf05471e16d..5df6b828f58 100644 --- a/Tests/StreamChatTests/APIClient/Endpoints/Payloads/MessagePayloads_Tests.swift +++ b/Tests/StreamChatTests/APIClient/Endpoints/Payloads/MessagePayloads_Tests.swift @@ -170,6 +170,28 @@ final class MessageRequestBody_Tests: XCTestCase { AssertJSONEqual(serializedJSON, expectedJSON) } + func test_isSerialized_whenSystemMessage() throws { + let payload: MessageRequestBody = .init( + id: .unique, + user: .dummy(userId: .unique), + text: "Announcement: The Death Star will be operational in 2 weeks.", + type: MessageType.system.rawValue, + extraData: [:] + ) + + let serializedJSON = try JSONEncoder.stream.encode(payload) + let expected: [String: Any] = [ + "id": payload.id, + "text": payload.text, + "type": "system", + "silent": false, + "pinned": false, + "show_in_channel": false + ] + let expectedJSON = try JSONSerialization.data(withJSONObject: expected, options: []) + AssertJSONEqual(serializedJSON, expectedJSON) + } + /// Check whether the message body is serialized when `isSilent` is not provided in `init` func test_isSerializedWithoutSilent() throws { let payload: MessageRequestBody = .init( diff --git a/Tests/StreamChatTests/Controllers/ChannelController/ChannelController_Tests.swift b/Tests/StreamChatTests/Controllers/ChannelController/ChannelController_Tests.swift index 21025f8b58b..492e6d16fab 100644 --- a/Tests/StreamChatTests/Controllers/ChannelController/ChannelController_Tests.swift +++ b/Tests/StreamChatTests/Controllers/ChannelController/ChannelController_Tests.swift @@ -3438,6 +3438,35 @@ final class ChannelController_Tests: XCTestCase { XCTAssertTrue(event is NewMessagePendingEvent) } + // MARK: - Create system message + + func test_createSystemMessage_callsChannelUpdater() { + let systemMessage = ChatMessage.mock(type: .system) + + // New message values + let text: String = .unique + + // Simulate `createNewMessage` calls and catch the completion + var completionCalled = false + controller.createSystemMessage( + text: text + ) { [callbackQueueID] result in + AssertTestQueue(withId: callbackQueueID) + AssertResultSuccess(result, systemMessage.id) + completionCalled = true + } + + XCTAssertFalse(completionCalled) + XCTAssertEqual(env.channelUpdater?.createNewMessage_cid, channelId) + XCTAssertEqual(env.channelUpdater?.createNewMessage_text, text) + XCTAssertEqual(env.channelUpdater?.createNewMessage_isSystem, true) + + // Simulate successful update + env.channelUpdater?.createNewMessage_completion?(.success(systemMessage)) + // Release reference of completion so we can deallocate stuff + env.channelUpdater!.createNewMessage_completion = nil + } + // MARK: - Adding members func test_addMembers_failsForNewChannels() throws { diff --git a/Tests/StreamChatTests/Database/DTOs/MessageDTO_Tests.swift b/Tests/StreamChatTests/Database/DTOs/MessageDTO_Tests.swift index ba9489668b7..567ba869b67 100644 --- a/Tests/StreamChatTests/Database/DTOs/MessageDTO_Tests.swift +++ b/Tests/StreamChatTests/Database/DTOs/MessageDTO_Tests.swift @@ -1229,7 +1229,6 @@ final class MessageDTO_Tests: XCTestCase { } let messageDTO: MessageDTO = try XCTUnwrap(database.viewContext.message(id: messageId)) - XCTAssertEqual(messageDTO.attachments.count, 4) // Load the message from the database and convert to request body. let requestBody: MessageRequestBody = messageDTO.asRequestBody() @@ -1249,6 +1248,58 @@ final class MessageDTO_Tests: XCTestCase { XCTAssertEqual(requestBody.attachments.map(\.type), [.image, .video]) XCTAssertEqual(requestBody.attachments.count, 2) XCTAssertEqual(requestBody.mentionedUserIds, mentionedUserIds) + XCTAssertEqual(requestBody.type, nil) + } + + func test_newMessage_asRequestBody_whenSystemMessage() throws { + let currentUserId: UserId = .unique + let cid: ChannelId = .unique + let parentMessageId: MessageId = .unique + + // Create current user in the database. + try database.createCurrentUser(id: currentUserId) + + // Create channel in the database. + try database.createChannel(cid: cid, withMessages: false) + + let messageId: MessageId = .unique + let messageText: String = .unique + let messageIsSystem = true + + // Create message with attachments in the database. + try database.writeSynchronously { session in + let message = try session.createNewMessage( + in: cid, + messageId: messageId, + text: messageText, + pinning: nil, + command: nil, + arguments: nil, + parentMessageId: nil, + attachments: [], + mentionedUserIds: [], + showReplyInChannel: false, + isSilent: false, + isSystem: messageIsSystem, + quotedMessageId: nil, + createdAt: nil, + skipPush: false, + skipEnrichUrl: false, + poll: nil, + extraData: [:] + ) + } + + let messageDTO: MessageDTO = try XCTUnwrap(database.viewContext.message(id: messageId)) + + // Load the message from the database and convert to request body. + let requestBody: MessageRequestBody = messageDTO.asRequestBody() + + // Assert request body has correct fields. + XCTAssertEqual(requestBody.id, messageId) + XCTAssertEqual(requestBody.user.id, currentUserId) + XCTAssertEqual(requestBody.text, messageText) + XCTAssertEqual(requestBody.type, "system") } func test_additionalLocalState_isStored() throws { @@ -1516,6 +1567,7 @@ final class MessageDTO_Tests: XCTestCase { let messageDTO: MessageDTO = try XCTUnwrap(database.viewContext.message(id: newMessageId)) XCTAssertEqual(messageDTO.skipPush, true) XCTAssertEqual(messageDTO.skipEnrichUrl, true) + XCTAssertEqual(messageDTO.isSystem, false) let loadedMessage: ChatMessage = try messageDTO.asModel() XCTAssertEqual(loadedMessage.text, newMessageText) @@ -1537,6 +1589,60 @@ final class MessageDTO_Tests: XCTestCase { XCTAssertEqual(loadedChannel.previewMessage?.id, loadedMessage.id) } + func test_createNewMessage_whenIsSystemMessage() throws { + // Prepare the current user and channel first + let cid: ChannelId = .unique + let currentUserId: UserId = .unique + + try database.writeSynchronously { session in + let currentUserPayload: CurrentUserPayload = .dummy( + userId: currentUserId, + role: .admin, + extraData: [:] + ) + + try session.saveCurrentUser(payload: currentUserPayload) + + try session.saveChannel(payload: self.dummyPayload(with: cid)) + } + + // Create a new message + var newMessageId: MessageId! + let newMessageText: String = .unique + try database.writeSynchronously { session in + let messageDTO = try session.createNewMessage( + in: cid, + messageId: .unique, + text: newMessageText, + pinning: nil, + command: nil, + arguments: nil, + parentMessageId: nil, + attachments: [], + mentionedUserIds: [], + showReplyInChannel: true, + isSilent: false, + isSystem: true, + quotedMessageId: nil, + createdAt: nil, + skipPush: true, + skipEnrichUrl: true, + poll: nil, + extraData: [:] + ) + newMessageId = messageDTO.id + } + + let loadedChannel: ChatChannel = try XCTUnwrap(database.viewContext.channel(cid: cid)).asModel() + + let messageDTO: MessageDTO = try XCTUnwrap(database.viewContext.message(id: newMessageId)) + XCTAssertEqual(messageDTO.isSystem, true) + + let loadedMessage: ChatMessage = try messageDTO.asModel() + XCTAssertEqual(loadedMessage.text, newMessageText) + XCTAssertEqual(loadedMessage.type, .system) + } + func test_createNewMessage_whenRegularMessageIsCreated_makesItChannelPreview() throws { // GIVEN let cid: ChannelId = .unique From 53583069b3227808231f426b77a964bc8f94c09c Mon Sep 17 00:00:00 2001 From: Nuno Vieira Date: Wed, 30 Oct 2024 15:36:20 +0000 Subject: [PATCH 08/10] Fix StreamChatUI test compilation --- .../Extensions/ChatMessage+Equatable_Tests.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Tests/StreamChatUITests/Extensions/ChatMessage+Equatable_Tests.swift b/Tests/StreamChatUITests/Extensions/ChatMessage+Equatable_Tests.swift index 7caa1886d4e..7718448a36a 100644 --- a/Tests/StreamChatUITests/Extensions/ChatMessage+Equatable_Tests.swift +++ b/Tests/StreamChatUITests/Extensions/ChatMessage+Equatable_Tests.swift @@ -131,6 +131,7 @@ final class ChatMessage_Equatable_Tests: XCTestCase { text: changingText ? "edited-message-\(index)" : "message\(index)", pinning: nil, quotedMessageId: nil, + isSystem: false, skipPush: true, skipEnrichUrl: true ) From c4058e4e4c691822550e3880b31c547397853dee Mon Sep 17 00:00:00 2001 From: Nuno Vieira Date: Wed, 30 Oct 2024 15:52:54 +0000 Subject: [PATCH 09/10] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f38300c810c..05c7ff85eb5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## StreamChat ### ✅ Added - Add support for system messages not updating `channel.lastMessageAt` [#3476](https://github.com/GetStream/stream-chat-swift/pull/3476) +- Add support for sending system messages client-side +[#3477](https://github.com/GetStream/stream-chat-swift/pull/3477) ### 🐞 Fixed - Fix watching channels when performing channel search [#3472](https://github.com/GetStream/stream-chat-swift/pull/3472) From e5c1ed7a861ca959d77bea8868a040ea27ce5e67 Mon Sep 17 00:00:00 2001 From: Nuno Vieira Date: Wed, 30 Oct 2024 17:22:15 +0000 Subject: [PATCH 10/10] Refactor implementation to not use `MessageDTO.isSystem` and only use `MessageDTO.type` --- .../StreamChat/Database/DTOs/MessageDTO.swift | 21 ++++++++++--------- .../StreamChatModel.xcdatamodel/contents | 3 +-- .../Database/DTOs/MessageDTO_Tests.swift | 7 +++---- .../Workers/ChannelUpdater_Tests.swift | 2 +- 4 files changed, 16 insertions(+), 17 deletions(-) diff --git a/Sources/StreamChat/Database/DTOs/MessageDTO.swift b/Sources/StreamChat/Database/DTOs/MessageDTO.swift index cccee654a26..690fa340d2f 100644 --- a/Sources/StreamChat/Database/DTOs/MessageDTO.swift +++ b/Sources/StreamChat/Database/DTOs/MessageDTO.swift @@ -39,9 +39,6 @@ class MessageDTO: NSManagedObject { @NSManaged var extraData: Data? @NSManaged var isSilent: Bool - // Used for creating a message as a system message from the client SDK. - @NSManaged var isSystem: Bool - @NSManaged var skipPush: Bool @NSManaged var skipEnrichUrl: Bool @NSManaged var isShadowed: Bool @@ -659,23 +656,27 @@ extension NSManagedObjectContext: MessageDatabaseSession { } message.cid = cid.rawValue - message.type = parentMessageId == nil ? MessageType.regular.rawValue : MessageType.reply.rawValue message.text = text message.command = command message.args = arguments message.parentMessageId = parentMessageId message.extraData = try JSONEncoder.default.encode(extraData) message.isSilent = isSilent - message.isSystem = isSystem - if isSystem { - message.type = MessageType.system.rawValue - } message.skipPush = skipPush message.skipEnrichUrl = skipEnrichUrl message.reactionScores = [:] message.reactionCounts = [:] message.reactionGroups = [] - + + // Message type + if parentMessageId != nil { + message.type = MessageType.reply.rawValue + } else if isSystem { + message.type = MessageType.system.rawValue + } else { + message.type = MessageType.regular.rawValue + } + if let poll { message.poll = try? savePoll(payload: poll, cache: nil) } @@ -1258,7 +1259,7 @@ extension MessageDTO { .compactMap { $0.asRequestPayload() } // At the moment, we only provide the type for system messages when creating a message. - let systemType = isSystem ? MessageType.system.rawValue : nil + let systemType = type == MessageType.system.rawValue ? type : nil return .init( id: id, diff --git a/Sources/StreamChat/Database/StreamChatModel.xcdatamodeld/StreamChatModel.xcdatamodel/contents b/Sources/StreamChat/Database/StreamChatModel.xcdatamodeld/StreamChatModel.xcdatamodel/contents index ac1e575aab3..f9e9e39810a 100644 --- a/Sources/StreamChat/Database/StreamChatModel.xcdatamodeld/StreamChatModel.xcdatamodel/contents +++ b/Sources/StreamChat/Database/StreamChatModel.xcdatamodeld/StreamChatModel.xcdatamodel/contents @@ -1,5 +1,5 @@ - + @@ -208,7 +208,6 @@ - diff --git a/Tests/StreamChatTests/Database/DTOs/MessageDTO_Tests.swift b/Tests/StreamChatTests/Database/DTOs/MessageDTO_Tests.swift index 567ba869b67..a5b1c1b27bb 100644 --- a/Tests/StreamChatTests/Database/DTOs/MessageDTO_Tests.swift +++ b/Tests/StreamChatTests/Database/DTOs/MessageDTO_Tests.swift @@ -1254,7 +1254,6 @@ final class MessageDTO_Tests: XCTestCase { func test_newMessage_asRequestBody_whenSystemMessage() throws { let currentUserId: UserId = .unique let cid: ChannelId = .unique - let parentMessageId: MessageId = .unique // Create current user in the database. try database.createCurrentUser(id: currentUserId) @@ -1268,7 +1267,7 @@ final class MessageDTO_Tests: XCTestCase { // Create message with attachments in the database. try database.writeSynchronously { session in - let message = try session.createNewMessage( + try session.createNewMessage( in: cid, messageId: messageId, text: messageText, @@ -1567,7 +1566,7 @@ final class MessageDTO_Tests: XCTestCase { let messageDTO: MessageDTO = try XCTUnwrap(database.viewContext.message(id: newMessageId)) XCTAssertEqual(messageDTO.skipPush, true) XCTAssertEqual(messageDTO.skipEnrichUrl, true) - XCTAssertEqual(messageDTO.isSystem, false) + XCTAssertEqual(messageDTO.type, "reply") let loadedMessage: ChatMessage = try messageDTO.asModel() XCTAssertEqual(loadedMessage.text, newMessageText) @@ -1636,7 +1635,7 @@ final class MessageDTO_Tests: XCTestCase { let loadedChannel: ChatChannel = try XCTUnwrap(database.viewContext.channel(cid: cid)).asModel() let messageDTO: MessageDTO = try XCTUnwrap(database.viewContext.message(id: newMessageId)) - XCTAssertEqual(messageDTO.isSystem, true) + XCTAssertEqual(messageDTO.type, "system") let loadedMessage: ChatMessage = try messageDTO.asModel() XCTAssertEqual(loadedMessage.text, newMessageText) diff --git a/Tests/StreamChatTests/Workers/ChannelUpdater_Tests.swift b/Tests/StreamChatTests/Workers/ChannelUpdater_Tests.swift index fcd64a7532e..81e74df1b0f 100644 --- a/Tests/StreamChatTests/Workers/ChannelUpdater_Tests.swift +++ b/Tests/StreamChatTests/Workers/ChannelUpdater_Tests.swift @@ -609,7 +609,7 @@ final class ChannelUpdater_Tests: XCTestCase { XCTAssertEqual(messageDTO.skipPush, true) XCTAssertEqual(messageDTO.skipEnrichUrl, true) XCTAssertEqual(messageDTO.mentionedUserIds, [currentUserId]) - XCTAssertEqual(messageDTO.isSystem, false) + XCTAssertEqual(messageDTO.type, "regular") let message = try messageDTO.asModel() XCTAssertEqual(message.text, text)