diff --git a/Linphone/UI/Main/Conversations/Fragments/ChatBubbleView.swift b/Linphone/UI/Main/Conversations/Fragments/ChatBubbleView.swift index b8697a70b..5b9250c7b 100644 --- a/Linphone/UI/Main/Conversations/Fragments/ChatBubbleView.swift +++ b/Linphone/UI/Main/Conversations/Fragments/ChatBubbleView.swift @@ -110,6 +110,30 @@ struct ChatBubbleView: View { .foregroundStyle(Color.grayMain2c700) .default_text_style(styleSize: 16) } + + HStack(alignment: .center) { + Text(conversationViewModel.getMessageTime(startDate: message.dateReceived)) + .foregroundStyle(Color.grayMain2c500) + .default_text_style_300(styleSize: 14) + .padding(.top, 1) + + if (conversationViewModel.displayedConversation != nil && conversationViewModel.displayedConversation!.isGroup) || message.isOutgoing { + if message.status == .sending { + ProgressView() + .progressViewStyle(CircularProgressViewStyle(tint: .orangeMain500)) + .frame(width: 15, height: 15) + .padding(.top, 1) + } else if message.status != nil { + Image(conversationViewModel.getImageIMDN(status: message.status!)) + .renderingMode(.template) + .resizable() + .foregroundStyle(Color.orangeMain500) + .frame(width: 15, height: 15) + .padding(.top, 1) + } + } + } + .padding(.top, -4) } .padding(.all, 15) .background(message.isOutgoing ? Color.orangeMain100 : Color.grayMain2c100) diff --git a/Linphone/UI/Main/Conversations/Model/Message.swift b/Linphone/UI/Main/Conversations/Model/Message.swift index 7ed606a4b..adbd9bc78 100644 --- a/Linphone/UI/Main/Conversations/Model/Message.swift +++ b/Linphone/UI/Main/Conversations/Model/Message.swift @@ -24,6 +24,7 @@ public struct Message: Identifiable, Hashable { public enum Status: Equatable, Hashable { case sending case sent + case received case read case error(DraftMessage) @@ -33,6 +34,8 @@ public struct Message: Identifiable, Hashable { return hasher.combine("sending") case .sent: return hasher.combine("sent") + case .received: + return hasher.combine("received") case .read: return hasher.combine("read") case .error: @@ -46,6 +49,8 @@ public struct Message: Identifiable, Hashable { return true case (.sent, .sent): return true + case (.received, .received): + return true case (.read, .read): return true case ( .error(_), .error(_)): @@ -60,6 +65,7 @@ public struct Message: Identifiable, Hashable { public var status: Status? public var createdAt: Date public var isOutgoing: Bool + public var dateReceived: time_t public var address: String public var isFirstMessage: Bool @@ -73,6 +79,7 @@ public struct Message: Identifiable, Hashable { status: Status? = nil, createdAt: Date = Date(), isOutgoing: Bool, + dateReceived: time_t, address: String, isFirstMessage: Bool = false, text: String = "", @@ -84,6 +91,7 @@ public struct Message: Identifiable, Hashable { self.status = status self.createdAt = createdAt self.isOutgoing = isOutgoing + self.dateReceived = dateReceived self.isFirstMessage = isFirstMessage self.address = address self.text = text @@ -117,6 +125,7 @@ public struct Message: Identifiable, Hashable { status: status, createdAt: draft.createdAt, isOutgoing: draft.isOutgoing, + dateReceived: draft.dateReceived, address: draft.address, isFirstMessage: draft.isFirstMessage, text: draft.text, @@ -162,6 +171,7 @@ public struct ReplyMessage: Codable, Identifiable, Hashable { public var isFirstMessage: Bool public var text: String public var isOutgoing: Bool + public var dateReceived: time_t public var attachments: [Attachment] public var recording: Recording? @@ -170,6 +180,7 @@ public struct ReplyMessage: Codable, Identifiable, Hashable { isFirstMessage: Bool = false, text: String = "", isOutgoing: Bool, + dateReceived: time_t, attachments: [Attachment] = [], recording: Recording? = nil) { @@ -178,25 +189,27 @@ public struct ReplyMessage: Codable, Identifiable, Hashable { self.isFirstMessage = isFirstMessage self.text = text self.isOutgoing = isOutgoing + self.dateReceived = dateReceived self.attachments = attachments self.recording = recording } func toMessage() -> Message { - Message(id: id, isOutgoing: isOutgoing, address: address, isFirstMessage: isFirstMessage, text: text, attachments: attachments, recording: recording) + Message(id: id, isOutgoing: isOutgoing, dateReceived: dateReceived, address: address, isFirstMessage: isFirstMessage, text: text, attachments: attachments, recording: recording) } } public extension Message { func toReplyMessage() -> ReplyMessage { - ReplyMessage(id: id, address: address, isFirstMessage: isFirstMessage, text: text, isOutgoing: isOutgoing, attachments: attachments, recording: recording) + ReplyMessage(id: id, address: address, isFirstMessage: isFirstMessage, text: text, isOutgoing: isOutgoing, dateReceived: dateReceived, attachments: attachments, recording: recording) } } public struct DraftMessage { public var id: String? public let isOutgoing: Bool + public var dateReceived: time_t public let address: String public let isFirstMessage: Bool public let text: String @@ -207,6 +220,7 @@ public struct DraftMessage { public init(id: String? = nil, isOutgoing: Bool, + dateReceived: time_t, address: String, isFirstMessage: Bool, text: String, @@ -216,6 +230,7 @@ public struct DraftMessage { createdAt: Date) { self.id = id self.isOutgoing = isOutgoing + self.dateReceived = dateReceived self.address = address self.isFirstMessage = isFirstMessage self.text = text diff --git a/Linphone/UI/Main/Conversations/ViewModel/ConversationViewModel.swift b/Linphone/UI/Main/Conversations/ViewModel/ConversationViewModel.swift index ba6ffab8a..390276357 100644 --- a/Linphone/UI/Main/Conversations/ViewModel/ConversationViewModel.swift +++ b/Linphone/UI/Main/Conversations/ViewModel/ConversationViewModel.swift @@ -31,7 +31,6 @@ class ConversationViewModel: ObservableObject { @Published var displayedConversationHistorySize: Int = 0 @Published var displayedConversationUnreadMessagesCount: Int = 0 - @Published var messageText: String = "" private var chatRoomSuscriptions = Set() @@ -151,10 +150,26 @@ class ConversationViewModel: ObservableObject { let isFirstMessageTmp = (eventLog.chatMessage?.isOutgoing ?? false) ? isFirstMessageOutgoingTmp : isFirstMessageIncomingTmp + var statusTmp: Message.Status? = .sending + switch eventLog.chatMessage?.state { + case .InProgress: + statusTmp = .sending + case .Delivered: + statusTmp = .sent + case .DeliveredToUser: + statusTmp = .received + case .Displayed: + statusTmp = .read + default: + statusTmp = nil + } + conversationMessage.append( Message( id: UUID().uuidString, + status: statusTmp, isOutgoing: eventLog.chatMessage?.isOutgoing ?? false, + dateReceived: eventLog.chatMessage?.time ?? 0, address: addressCleaned?.asStringUriOnly() ?? "", isFirstMessage: isFirstMessageTmp, text: contentText, @@ -204,10 +219,26 @@ class ConversationViewModel: ObservableObject { let isFirstMessageTmp = (eventLog.chatMessage?.isOutgoing ?? false) ? isFirstMessageOutgoingTmp : isFirstMessageIncomingTmp + var statusTmp: Message.Status? = .sending + switch eventLog.chatMessage?.state { + case .InProgress: + statusTmp = .sending + case .Delivered: + statusTmp = .sent + case .DeliveredToUser: + statusTmp = .received + case .Displayed: + statusTmp = .read + default: + statusTmp = nil + } + conversationMessagesTmp.insert( Message( id: UUID().uuidString, + status: statusTmp, isOutgoing: eventLog.chatMessage?.isOutgoing ?? false, + dateReceived: eventLog.chatMessage?.time ?? 0, address: addressCleaned?.asStringUriOnly() ?? "", isFirstMessage: isFirstMessageTmp, text: contentText, @@ -277,9 +308,25 @@ class ConversationViewModel: ObservableObject { let isFirstMessageTmp = (eventLog.chatMessage?.isOutgoing ?? false) ? isFirstMessageOutgoingTmp : isFirstMessageIncomingTmp + var statusTmp: Message.Status? = .sending + switch eventLog.chatMessage?.state { + case .InProgress: + statusTmp = .sending + case .Delivered: + statusTmp = .sent + case .DeliveredToUser: + statusTmp = .received + case .Displayed: + statusTmp = .read + default: + statusTmp = nil + } + let message = Message( id: UUID().uuidString, + status: statusTmp, isOutgoing: eventLog.chatMessage?.isOutgoing ?? false, + dateReceived: eventLog.chatMessage?.time ?? 0, address: addressCleaned?.asStringUriOnly() ?? "", isFirstMessage: isFirstMessageTmp, text: contentText, @@ -426,6 +473,42 @@ class ConversationViewModel: ObservableObject { func getNewFilePath(name: String) -> String { return "file://" + Factory.Instance.getDownloadDir(context: nil) + name } + + func getMessageTime(startDate: time_t) -> String { + let timeInterval = TimeInterval(startDate) + + let myNSDate = Date(timeIntervalSince1970: timeInterval) + + if Calendar.current.isDateInToday(myNSDate) { + let formatter = DateFormatter() + formatter.dateFormat = Locale.current.identifier == "fr_FR" ? "HH:mm" : "h:mm a" + return formatter.string(from: myNSDate) + } else if Calendar.current.isDate(myNSDate, equalTo: .now, toGranularity: .year) { + let formatter = DateFormatter() + formatter.dateFormat = Locale.current.identifier == "fr_FR" ? "dd/MM HH:mm" : "MM/dd h:mm a" + return formatter.string(from: myNSDate) + } else { + let formatter = DateFormatter() + formatter.dateFormat = Locale.current.identifier == "fr_FR" ? "dd/MM/yy HH:mm" : "MM/dd/yy h:mm a" + return formatter.string(from: myNSDate) + } + } + + func getImageIMDN(status: Message.Status) -> String { + switch status { + case .sending: + return "" + case .sent: + return "envelope-simple" + case .received: + return "check" + case .read: + return "checks" + case .error: + return "" + } + } + } struct LinphoneCustomEventLog: Hashable { var id = UUID()