AzureCommunicationUI/sdk/AzureCommunicationUIChat/Sources/Presentation/Manager/MessageRepositoryManager.swift (187 lines of code) (raw):
//
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
//
import Foundation
import AzureCore
protocol MessageRepositoryManagerProtocol {
var messages: [ChatMessageInfoModel] { get }
// MARK: sending local events
func addInitialMessages(initialMessages: [ChatMessageInfoModel])
func addPreviousMessages(previousMessages: [ChatMessageInfoModel])
func addNewSendingMessage(message: ChatMessageInfoModel)
func editMessage(messageId: String, content: String)
func deleteMessage(messageId: String)
func replaceMessageId(internalId: String, actualId: String)
func addLocalUserRemovedMessage()
func updateEditMessageTimestamp(messageId: String)
func updateDeletedMessageTimestamp(messageId: String)
// MARK: participant events
func addParticipantAdded(message: ChatMessageInfoModel)
func addParticipantRemoved(message: ChatMessageInfoModel)
// MARK: receiving remote events
func addTopicUpdatedMessage(chatThreadInfo: ChatThreadInfoModel)
func addReceivedMessage(message: ChatMessageInfoModel)
func updateMessageEdited(message: ChatMessageInfoModel)
func updateMessageDeleted(message: ChatMessageInfoModel)
func updateMessageReadReceiptStatus(readReceiptInfo: ReadReceiptInfoModel, state: ChatAppState)
func updateMessageSendStatus(messageId: String, messageSendStatus: MessageSendStatus)
}
class MessageRepositoryManager: MessageRepositoryManagerProtocol {
var messages: [ChatMessageInfoModel] = []
var initialFetchTimestamp = Iso8601Date()
private let eventsHandler: ChatAdapter.Events
init(chatCompositeEventsHandler: ChatAdapter.Events) {
self.eventsHandler = chatCompositeEventsHandler
}
func addInitialMessages(initialMessages: [ChatMessageInfoModel]) {
messages = initialMessages
messages.sort { lhs, rhs -> Bool in
// createdOn does not have milliseconds
return lhs.createdOn == rhs.createdOn ?
lhs.id < rhs.id : lhs.createdOn < rhs.createdOn
}
// Assume all previously sent messages have been seen
if let index = messages.lastIndex(where: {$0.isLocalUser}) {
messages[index].update(sendStatus: .seen)
}
initialFetchTimestamp = Iso8601Date()
}
func addPreviousMessages(previousMessages: [ChatMessageInfoModel]) {
// Workaround: improve data structure in MessageRepo user story
for message in previousMessages {
if let index = messages.firstIndex(where: {
$0.id == message.id
}) {
messages[index] = message
} else if message.createdOn < initialFetchTimestamp {
// Add all previous message
messages.append(message)
} else if !message.type.isSystemMessage {
// Workaround: for system message, use trouter msg, drop new fetched message after initialFetchTimestamp
messages.append(message)
}
}
messages.sort { lhs, rhs -> Bool in
// createdOn does not have milliseconds
return lhs.createdOn == rhs.createdOn ?
lhs.id < rhs.id : lhs.createdOn < rhs.createdOn
}
}
func addNewSendingMessage(message: ChatMessageInfoModel) {
messages.append(message)
}
func editMessage(messageId: String, content: String) {
if let index = messages.firstIndex(where: {
$0.id == messageId
}) {
var msg = messages[index]
msg.edit(content: content)
msg.update(editedOn: Iso8601Date())
messages[index] = msg
}
}
func deleteMessage(messageId: String) {
if let index = messages.firstIndex(where: {
$0.id == messageId
}) {
var msg = messages[index]
msg.update(deletedOn: Iso8601Date())
messages[index] = msg
}
}
func replaceMessageId(internalId: String, actualId: String) {
if let index = messages.firstIndex(where: {
$0.id == internalId
}) {
var msg = messages[index]
msg.replace(id: actualId)
messages[index] = msg
}
}
func updateEditMessageTimestamp(messageId: String) {
if let index = messages.firstIndex(where: {
$0.id == messageId
}) {
var msg = messages[index]
msg.update(editedOn: Iso8601Date())
messages[index] = msg
}
}
func updateDeletedMessageTimestamp(messageId: String) {
if let index = messages.firstIndex(where: {
$0.id == messageId
}) {
var msg = messages[index]
msg.update(deletedOn: Iso8601Date())
messages[index] = msg
}
}
func addParticipantAdded(message: ChatMessageInfoModel) {
messages.append(message)
}
func addParticipantRemoved(message: ChatMessageInfoModel) {
messages.append(message)
}
func addTopicUpdatedMessage(chatThreadInfo: ChatThreadInfoModel) {
guard let topic = chatThreadInfo.topic else {
return
}
let topicUpdatedSystemMessage = ChatMessageInfoModel(
type: .topicUpdated,
content: topic,
createdOn: chatThreadInfo.receivedOn
)
messages.append(topicUpdatedSystemMessage)
}
func addLocalUserRemovedMessage() {
let localUserRemovedSystemMessage = ChatMessageInfoModel(
type: .participantsRemoved,
createdOn: Iso8601Date(),
isLocalUser: true
)
messages.append(localUserRemovedSystemMessage)
}
func addReceivedMessage(message: ChatMessageInfoModel) {
guard !message.isLocalUser else {
return
}
if let index = messages.firstIndex(where: {
$0.id == message.id
}) {
messages[index] = message
} else {
messages.append(message)
}
}
func updateMessageEdited(message: ChatMessageInfoModel) {
if let index = messages.firstIndex(where: {
$0.id == message.id
}) {
messages[index] = message
}
}
func updateMessageDeleted(message: ChatMessageInfoModel) {
if let index = messages.firstIndex(where: {
$0.id == message.id
}) {
messages[index] = message
}
}
func updateMessageSendStatus(messageId: String, messageSendStatus: MessageSendStatus) {
guard let index = messages.firstIndex(where: {
$0.id == messageId
}) else {
return
}
messages[index].update(sendStatus: messageSendStatus)
}
func updateMessageReadReceiptStatus(readReceiptInfo: ReadReceiptInfoModel, state: ChatAppState) {
guard readReceiptInfo.senderIdentifier.stringValue != state.chatState.localUser?.identifier.stringValue else {
return
}
let messageId = readReceiptInfo.chatMessageId
let messageTimestamp = messageId.convertEpochStringToTimestamp()
var readReceiptMap = state.participantsState.readReceiptMap
readReceiptMap[readReceiptInfo.senderIdentifier.stringValue] = messageTimestamp
let minimumReadReceiptTimestamp = readReceiptMap.min { $0.value < $1.value }?.value
guard let minimumReadReceiptTimestamp = minimumReadReceiptTimestamp else {
return
}
guard let messageTimestamp = messageTimestamp,
messageTimestamp <= minimumReadReceiptTimestamp,
let index = messages.firstIndex(where: {
$0.id == messageId
}) else {
return
}
messages[index].update(sendStatus: .seen)
}
}