Skip to content

Commit

Permalink
Change the approach: Do not create a new event for Hard Deleted
Browse files Browse the repository at this point in the history
  • Loading branch information
nuno-vieira committed Jan 24, 2025
1 parent 15560f8 commit 31ccbec
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 38 deletions.
2 changes: 1 addition & 1 deletion DemoApp/StreamChat/Components/DemoChatThreadVC.swift
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ class DemoChatThreadVC: ChatThreadVC, CurrentChatUserControllerDelegate {
super.eventsController(controller, didReceiveEvent: event)

// Dismiss the thread if the root message was hard deleted.
if let event = event as? MessageHardDeletedEvent, event.messageId == messageController.messageId {
if let event = event as? MessageDeletedEvent, event.isHardDelete, event.message.id == messageController.messageId {
navigationController?.popViewController(animated: true)
}
}
Expand Down
109 changes: 77 additions & 32 deletions Sources/StreamChat/WebSocketClient/Events/MessageEvents.swift
Original file line number Diff line number Diff line change
Expand Up @@ -133,28 +133,9 @@ public struct MessageDeletedEvent: ChannelSpecificEvent {
public let createdAt: Date

/// A Boolean value indicating whether it is an hard delete or not.
@available(*, deprecated, message: "The `MessageHardDeletedEvent` should be used instead.")
public let isHardDelete: Bool
}

/// Triggered when a new message is hard deleted.
public struct MessageHardDeletedEvent: ChannelSpecificEvent {
/// The user who deleted the message.
public let user: ChatUser?

/// The channel identifier a message was deleted from.
public var cid: ChannelId { channel.cid }

/// The channel a message was deleted from.
public let channel: ChatChannel

/// The hard deleted message id.
public let messageId: MessageId

/// The event timestamp.
public let createdAt: Date
}

class MessageDeletedEventDTO: EventDTO {
let user: UserPayload?
let cid: ChannelId
Expand All @@ -178,24 +159,16 @@ class MessageDeletedEventDTO: EventDTO {
}

let userDTO = user.flatMap { session.user(id: $0.id) }
let messageDTO = session.message(id: message.id)

if hardDelete {
return try? MessageHardDeletedEvent(
user: userDTO?.asModel(),
channel: channelDTO.asModel(),
messageId: message.id,
createdAt: createdAt
)
}

guard let messageDTO = session.message(id: message.id) else {
return nil
}
// If the message is hard deleted, it is not available as DTO.
// So we map the Payload Directly to the Model.
let message = (try? messageDTO?.asModel()) ?? message.asModel()

return try? MessageDeletedEvent(
user: userDTO?.asModel(),
channel: channelDTO.asModel(),
message: messageDTO.asModel(),
message: message,
createdAt: createdAt,
isHardDelete: hardDelete
)
Expand Down Expand Up @@ -273,3 +246,75 @@ public struct NewMessageErrorEvent: Event {
public let messageId: MessageId
public let error: Error
}

// MARK: - Workaround to map a deleted message to Model.

// At the moment our SDK does not support mapping Payload -> Model
// So this is just a workaround for `MessageDeletedEvent` to have the `message` non-optional.
// So some of the data will be incorrect, but for this is use case is more than enough.

private extension MessagePayload {
func asModel() -> ChatMessage {
.init(
id: id,
cid: cid,
text: text,
type: type,
command: command,
createdAt: createdAt,
locallyCreatedAt: nil,
updatedAt: updatedAt,
deletedAt: deletedAt,
arguments: args,
parentMessageId: parentId,
showReplyInChannel: showReplyInChannel,
replyCount: replyCount,
extraData: extraData,
quotedMessage: quotedMessage?.asModel(),
isBounced: false,
isSilent: isSilent,
isShadowed: isShadowed,
reactionScores: reactionScores,
reactionCounts: reactionCounts,
reactionGroups: [:],
author: user.asModel(),
mentionedUsers: Set(mentionedUsers.map { $0.asModel() }),
threadParticipants: threadParticipants.map { $0.asModel() },
attachments: [],
latestReplies: [],
localState: nil,
isFlaggedByCurrentUser: false,
latestReactions: [],
currentUserReactions: [],
isSentByCurrentUser: false,
pinDetails: nil,
translations: nil,
originalLanguage: originalLanguage.map { TranslationLanguage(languageCode: $0) },
moderationDetails: nil,
readBy: [],
poll: nil,
textUpdatedAt: messageTextUpdatedAt
)
}
}

private extension UserPayload {
func asModel() -> ChatUser {
.init(
id: id,
name: name,
imageURL: imageURL,
isOnline: isOnline,
isBanned: isBanned,
isFlaggedByCurrentUser: false,
userRole: role,
createdAt: createdAt,
updatedAt: updatedAt,
deactivatedAt: deactivatedAt,
lastActiveAt: lastActiveAt,
teams: Set(teams),
language: language.map { TranslationLanguage(languageCode: $0) },
extraData: extraData
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ final class MessageEvents_Tests: XCTestCase {
XCTAssertEqual(event?.hardDelete, true)
}

func test_messageDeletedEvent_toDomainEvent_thenIsMessageDeletedEvent() throws {
func test_messageDeletedEvent_toDomainEvent() throws {
let json = XCTestCase.mockData(fromJSONFile: "MessageDeleted")
let event = try eventDecoder.decode(from: json) as? MessageDeletedEventDTO

Expand All @@ -96,18 +96,18 @@ final class MessageEvents_Tests: XCTestCase {
XCTAssertEqual(domainEvent is MessageDeletedEvent, true)
}

func test_messageDeletedEvent_toDomainEvent_whenIsHardDeleted_thenIsMessageHardDeletedEvent() throws {
func test_messageDeletedEvent_toDomainEvent_whenIsHardDeleted_whenMessageNotInLocalDB() throws {
let json = XCTestCase.mockData(fromJSONFile: "MessageDeletedHard")
let event = try eventDecoder.decode(from: json) as? MessageDeletedEventDTO

let channelId = try XCTUnwrap(event?.cid)
let message = try XCTUnwrap(event?.message)
let session = DatabaseContainer_Spy(kind: .inMemory).viewContext
// Only save the channel. Not the message. In this case the payload should be directly mapped to model.
_ = try session.saveChannel(payload: .dummy(cid: channelId), query: nil, cache: nil)
_ = try session.saveMessage(payload: message, for: channelId, cache: nil)

let domainEvent = event?.toDomainEvent(session: session)
XCTAssertEqual(domainEvent is MessageHardDeletedEvent, true)
let domainEvent = try XCTUnwrap(event?.toDomainEvent(session: session) as? MessageDeletedEvent)
XCTAssertEqual(domainEvent.message.id, message.id)
}

func test_read() throws {
Expand Down

0 comments on commit 31ccbec

Please sign in to comment.