AzureCommunicationUI/sdk/AzureCommunicationUIChat/Sources/Redux/Middleware/RepositoryMiddlewareHandler.swift (307 lines of code) (raw):
//
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
//
import AzureCommunicationCommon
import AzureCore
import Foundation
protocol RepositoryMiddlewareHandling {
@discardableResult
func loadInitialMessages(
messages: [ChatMessageInfoModel],
state: ChatAppState,
dispatch: @escaping ActionDispatch) -> Task<Void, Never>
@discardableResult
func addPreviousMessages(
messages: [ChatMessageInfoModel],
state: ChatAppState,
dispatch: @escaping ActionDispatch) -> Task<Void, Never>
@discardableResult
func addNewSentMessage(
internalId: String,
content: String,
state: ChatAppState,
dispatch: @escaping ActionDispatch) -> Task<Void, Never>
@discardableResult
func updateNewEditedMessage(
messageId: String,
content: String,
state: ChatAppState,
dispatch: @escaping ActionDispatch) -> Task<Void, Never>
@discardableResult
func updateNewDeletedMessage(
messageId: String,
state: ChatAppState,
dispatch: @escaping ActionDispatch) -> Task<Void, Never>
@discardableResult
func updateSentMessageIdAndSendStatus(
internalId: String,
actualId: String,
state: ChatAppState,
dispatch: @escaping ActionDispatch) -> Task<Void, Never>
@discardableResult
func updateEditedMessageTimestamp(
messageId: String,
state: ChatAppState,
dispatch: @escaping ActionDispatch) -> Task<Void, Never>
@discardableResult
func updateDeletedMessageTimestamp(
messageId: String,
state: ChatAppState,
dispatch: @escaping ActionDispatch) -> Task<Void, Never>
@discardableResult
func addTopicUpdatedMessage(
threadInfo: ChatThreadInfoModel,
state: ChatAppState,
dispatch: @escaping ActionDispatch) -> Task<Void, Never>
@discardableResult
func participantAddedMessage(participants: [ParticipantInfoModel],
state: ChatAppState,
dispatch: @escaping ActionDispatch) -> Task<Void, Never>
@discardableResult
func participantRemovedMessage(participants: [ParticipantInfoModel],
dispatch: @escaping ActionDispatch) -> Task<Void, Never>
@discardableResult
func addLocalUserRemovedMessage(state: ChatAppState,
dispatch: @escaping ActionDispatch) -> Task<Void, Never>
@discardableResult
func addReceivedMessage(
message: ChatMessageInfoModel,
state: ChatAppState,
dispatch: @escaping ActionDispatch) -> Task<Void, Never>
@discardableResult
func updateReceivedEditedMessage(
message: ChatMessageInfoModel,
state: ChatAppState,
dispatch: @escaping ActionDispatch) -> Task<Void, Never>
@discardableResult
func updateReceivedDeletedMessage(
message: ChatMessageInfoModel,
state: ChatAppState,
dispatch: @escaping ActionDispatch) -> Task<Void, Never>
@discardableResult
func updateMessageSendStatus(
messageId: String,
messageSendStatus: MessageSendStatus,
dispatch: @escaping ActionDispatch) -> Task<Void, Never>
@discardableResult
func updateMessageReceiptReceivedStatus(
readReceiptInfo: ReadReceiptInfoModel,
state: ChatAppState,
dispatch: @escaping ActionDispatch) -> Task<Void, Never>
}
class RepositoryMiddlewareHandler: RepositoryMiddlewareHandling {
private let messageRepository: MessageRepositoryManagerProtocol
private let logger: Logger
init(messageRepository: MessageRepositoryManagerProtocol,
logger: Logger) {
self.messageRepository = messageRepository
self.logger = logger
}
func loadInitialMessages(
messages: [ChatMessageInfoModel],
state: ChatAppState,
dispatch: @escaping ActionDispatch) -> Task<Void, Never> {
Task {
let filteredMessages = getMessagesWithoutMaskedParticipants(messages: messages, state: state)
messageRepository.addInitialMessages(initialMessages: filteredMessages)
dispatch(.repositoryAction(.repositoryUpdated))
}
}
func addPreviousMessages(
messages: [ChatMessageInfoModel],
state: ChatAppState,
dispatch: @escaping ActionDispatch) -> Task<Void, Never> {
Task {
let filteredMessages = getMessagesWithoutMaskedParticipants(messages: messages, state: state)
messageRepository.addPreviousMessages(previousMessages: filteredMessages)
dispatch(.repositoryAction(.repositoryUpdated))
}
}
func addNewSentMessage(
internalId: String,
content: String,
state: ChatAppState,
dispatch: @escaping ActionDispatch) -> Task<Void, Never> {
Task {
guard let localUserId = state.chatState.localUser?.identifier.stringValue,
let displayName = state.chatState.localUser?.displayName else {
return
}
let message = ChatMessageInfoModel(
id: internalId,
type: .text,
senderId: localUserId,
senderDisplayName: displayName,
content: content,
sendStatus: .sending,
isLocalUser: true)
messageRepository.addNewSendingMessage(message: message)
dispatch(.repositoryAction(.repositoryUpdated))
}
}
func updateNewEditedMessage(
messageId: String,
content: String,
state: ChatAppState,
dispatch: @escaping ActionDispatch) -> Task<Void, Never> {
Task {
messageRepository.editMessage(messageId: messageId, content: content)
dispatch(.repositoryAction(.repositoryUpdated))
}
}
func updateNewDeletedMessage(
messageId: String,
state: ChatAppState,
dispatch: @escaping ActionDispatch) -> Task<Void, Never> {
Task {
messageRepository.deleteMessage(messageId: messageId)
dispatch(.repositoryAction(.repositoryUpdated))
}
}
func updateSentMessageIdAndSendStatus(
internalId: String,
actualId: String,
state: ChatAppState,
dispatch: @escaping ActionDispatch) -> Task<Void, Never> {
Task {
messageRepository.replaceMessageId(internalId: internalId,
actualId: actualId)
messageRepository.updateMessageSendStatus(messageId: actualId, messageSendStatus: .sent)
dispatch(.repositoryAction(.repositoryUpdated))
}
}
func updateEditedMessageTimestamp(
messageId: String,
state: ChatAppState,
dispatch: @escaping ActionDispatch) -> Task<Void, Never> {
Task {
messageRepository.updateEditMessageTimestamp(messageId: messageId)
dispatch(.repositoryAction(.repositoryUpdated))
}
}
func updateDeletedMessageTimestamp(
messageId: String,
state: ChatAppState,
dispatch: @escaping ActionDispatch) -> Task<Void, Never> {
Task {
messageRepository.updateDeletedMessageTimestamp(messageId: messageId)
dispatch(.repositoryAction(.repositoryUpdated))
}
}
func addTopicUpdatedMessage(
threadInfo: ChatThreadInfoModel,
state: ChatAppState,
dispatch: @escaping ActionDispatch) -> Task<Void, Never> {
Task {
messageRepository.addTopicUpdatedMessage(chatThreadInfo: threadInfo)
dispatch(.repositoryAction(.repositoryUpdated))
}
}
func addLocalUserRemovedMessage(state: ChatAppState,
dispatch: @escaping ActionDispatch) -> Task<Void, Never> {
Task {
messageRepository.addLocalUserRemovedMessage()
dispatch(.repositoryAction(.repositoryUpdated))
}
}
func participantAddedMessage(participants: [ParticipantInfoModel],
state: ChatAppState,
dispatch: @escaping ActionDispatch) -> Task<Void, Never> {
Task {
let filteredParticipants = filterOutMaskedParticipantsFromMessage(participants: participants, state: state)
guard !filteredParticipants.isEmpty else {
return
}
let message = ChatMessageInfoModel(
type: .participantsAdded,
createdOn: Iso8601Date(),
participants: filteredParticipants)
messageRepository.addParticipantAdded(message: message)
dispatch(.repositoryAction(.repositoryUpdated))
}
}
func participantRemovedMessage(participants: [ParticipantInfoModel],
dispatch: @escaping ActionDispatch) -> Task<Void, Never> {
Task {
let message = ChatMessageInfoModel(
type: .participantsRemoved,
createdOn: Iso8601Date(),
participants: participants)
messageRepository.addParticipantRemoved(message: message)
dispatch(.repositoryAction(.repositoryUpdated))
}
}
func addReceivedMessage(
message: ChatMessageInfoModel,
state: ChatAppState,
dispatch: @escaping ActionDispatch) -> Task<Void, Never> {
Task {
messageRepository.addReceivedMessage(message: message)
dispatch(.repositoryAction(.repositoryUpdated))
}
}
func updateReceivedEditedMessage(
message: ChatMessageInfoModel,
state: ChatAppState,
dispatch: @escaping ActionDispatch) -> Task<Void, Never> {
Task {
messageRepository.updateMessageEdited(message: message)
dispatch(.repositoryAction(.repositoryUpdated))
}
}
func updateReceivedDeletedMessage(
message: ChatMessageInfoModel,
state: ChatAppState,
dispatch: @escaping ActionDispatch) -> Task<Void, Never> {
Task {
messageRepository.updateMessageDeleted(message: message)
dispatch(.repositoryAction(.repositoryUpdated))
}
}
func updateMessageSendStatus(
messageId: String,
messageSendStatus: MessageSendStatus,
dispatch: @escaping ActionDispatch) -> Task<Void, Never> {
Task {
messageRepository.updateMessageSendStatus(messageId: messageId, messageSendStatus: messageSendStatus)
dispatch(.repositoryAction(.repositoryUpdated))
}
}
func updateMessageReceiptReceivedStatus(
readReceiptInfo: ReadReceiptInfoModel,
state: ChatAppState,
dispatch: @escaping ActionDispatch) -> Task<Void, Never> {
Task {
messageRepository.updateMessageReadReceiptStatus(readReceiptInfo: readReceiptInfo, state: state)
dispatch(.repositoryAction(.repositoryUpdated))
}
}
private func getMessagesWithoutMaskedParticipants(
messages: [ChatMessageInfoModel],
state: ChatAppState) -> [ChatMessageInfoModel] {
var newMessages = messages
var messageIdsToRemove: Set<String> = []
for (index, message) in messages.enumerated() where message.type == .participantsAdded {
let filteredParticipants = filterOutMaskedParticipantsFromMessage(
participants: message.participants,
state: state)
guard !filteredParticipants.isEmpty else {
messageIdsToRemove.insert(message.id)
continue
}
var newMessage = message
newMessage.participants = filteredParticipants
newMessages[index] = newMessage
}
newMessages = newMessages.filter {
!messageIdsToRemove.contains($0.id)
}
return newMessages
}
private func filterOutMaskedParticipantsFromMessage(
participants: [ParticipantInfoModel],
state: ChatAppState) -> [ParticipantInfoModel] {
let maskedParticipants = state.participantsState.maskedParticipants
let participants = participants.filter {
!maskedParticipants.contains($0.id)
}
return participants
}
}