AzureCommunicationUI/sdk/AzureCommunicationUIChat/Sources/Model/ChatMessageInfoModel.swift (200 lines of code) (raw):

// // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. // import AzureCore enum MessageType: Equatable { case custom(String) case text case html case topicUpdated case participantsAdded case participantsRemoved var isSystemMessage: Bool { switch self { case .topicUpdated, .participantsAdded, .participantsRemoved: return true default: return false } } } enum MessageSendStatus: Equatable { case sending case sent case seen case failed } struct ChatMessageInfoModel: BaseInfoModel, Identifiable, Equatable, Hashable { var id: String let version: String let type: MessageType var senderId: String? var senderDisplayName: String? var rawContent: String? var content: String? var createdOn: Iso8601Date var editedOn: Iso8601Date? var deletedOn: Iso8601Date? var sendStatus: MessageSendStatus? var isLocalUser: Bool // for participant added/removed only var participants: [ParticipantInfoModel] init(id: String? = nil, version: String = "", type: MessageType = .text, senderId: String? = nil, senderDisplayName: String? = nil, content: String? = nil, createdOn: Iso8601Date? = nil, editedOn: Iso8601Date? = nil, deletedOn: Iso8601Date? = nil, participants: [ParticipantInfoModel] = [], sendStatus: MessageSendStatus? = nil, isLocalUser: Bool = false) { self.id = id ?? UUID().uuidString self.version = version self.type = type self.senderId = senderId self.senderDisplayName = senderDisplayName self.rawContent = content if type == .html { self.content = content?.unescapeHtmlString } else { self.content = content } self.createdOn = createdOn ?? Iso8601Date() self.editedOn = editedOn self.deletedOn = deletedOn self.participants = participants self.sendStatus = sendStatus self.isLocalUser = isLocalUser } mutating func replace(id: String) { self.id = id } mutating func edit(content: String) { self.content = content } mutating func update(editedOn: Iso8601Date) { self.editedOn = editedOn } mutating func update(deletedOn: Iso8601Date) { self.deletedOn = deletedOn } mutating func update(sendStatus: MessageSendStatus) { self.sendStatus = sendStatus } func hash(into hasher: inout Hasher) { hasher.combine(id) } } extension ChatMessageInfoModel { // Inject localization into method var dateHeaderLabel: String { let numberOfDaysSinceToday = createdOn.value.numberOfDays() if numberOfDaysSinceToday == 0 { return "Today" // Localization } else if numberOfDaysSinceToday == 1 { return "Yesterday" // Locatization } else if numberOfDaysSinceToday < 365 { let format = DateFormatter() format.dateFormat = "MMMM d" let formattedDate = format.string(from: createdOn.value) return formattedDate } else { let format = DateFormatter() format.dateFormat = "MMMM d, yyyy" let formattedDate = format.string(from: createdOn.value) return formattedDate } } // MARK: Text Message // Inject localization into method var timestamp: String { let createdOn = createdOn.value let dateFormatter = DateFormatter() dateFormatter.dateFormat = "h:mm a" dateFormatter.amSymbol = "a.m." // Localization? dateFormatter.pmSymbol = "p.m." // Localization? return dateFormatter.string(from: createdOn) } func getIconNameForMessageSendStatus() -> CompositeIcon? { guard isLocalUser, let sendStatus = sendStatus else { return nil } switch sendStatus { case .sending: return .messageSending case .sent: return .messageSent case .seen: return .readReceipt case .failed: return .messageSendFailed } } func getContentLabel() -> String { return content ?? "Text not available" // Localization } private func getTopicLabel() -> String { guard let topic = content else { // Localization return "Topic updated" } // Localization return "Topic updated to \"\(topic)\"" } // MARK: System Message // Inject localization into method var systemLabel: String { switch type { case .participantsAdded: return "\(participantsLabel) joined the chat" // Localization case .participantsRemoved: if isLocalUser { return "You were removed from the chat" // Localization } return "\(participantsLabel) left the chat" // Localization case .topicUpdated: return getTopicLabel() default: return "" } } var participantsLabel: String { return participants.map {$0.displayName} .joined(separator: ", ") } var systemIcon: CompositeIcon? { switch type { case .participantsAdded: return .systemJoin case .participantsRemoved: return .systemLeave case .topicUpdated: return nil default: return nil } } } extension ChatMessageInfoModel { func toChatMessage() -> ChatMessageModel { return ChatMessageModel( id: self.id, content: self.content ?? "not available", senderId: self.senderId ?? "", senderDisplayName: self.senderDisplayName ?? "") } } // To be public for event handlers struct ChatMessageModel { let id: String let content: String let senderId: String let senderDisplayName: String } extension Iso8601Date { var dayOfYear: Int { return Calendar.current.ordinality(of: .day, in: .year, for: self.value)! } } extension Date { func numberOfDays() -> Int { let calendar = Calendar.current let from = calendar.startOfDay(for: self) let to = calendar.startOfDay(for: Date()) let components = calendar.dateComponents([.day], from: from, to: to) return components.day! } }