New message deletion feature

This commit is contained in:
Benoit Martins 2025-11-20 16:48:38 +01:00
parent 07dbf407b0
commit 0daba4fe03
14 changed files with 272 additions and 86 deletions

View file

@ -474,9 +474,9 @@
"message_copied_to_clipboard_toast" = "Zpráva zkopírována do schránky";
"message_delivery_info_read_title" = "Přečteno";
"message_delivery_info_received_title" = "Přijato";
"message_meeting_invitation_cancelled_notification" = "📅 Schůzka byla zrušena";
"message_meeting_invitation_notification" = "📅 Jste pozváni na schůzku";
"message_meeting_invitation_updated_notification" = "📅 Schůzka byla aktualizována";
"message_meeting_invitation_cancelled_notification" = "Schůzka byla zrušena";
"message_meeting_invitation_notification" = "Jste pozváni na schůzku";
"message_meeting_invitation_updated_notification" = "Schůzka byla aktualizována";
"message_reactions_info_all_title" = "Reakce";
"network_reachable_again" = "Síť je znovu dostupná";
"menu_block_number" = "Blokovat číslo";

View file

@ -467,9 +467,9 @@
"message_delivery_info_read_title" = "Gelesen";
"message_delivery_info_received_title" = "Empfangen";
"message_delivery_info_sent_title" = "Gesendet";
"message_meeting_invitation_cancelled_notification" = "📅 Die Besprechung wurde abgesagt";
"message_meeting_invitation_notification" = "📅 Sie sind zu einer Besprechung eingeladen";
"message_meeting_invitation_updated_notification" = "📅 Besprechung wurde aktualisiert";
"message_meeting_invitation_cancelled_notification" = "Die Besprechung wurde abgesagt";
"message_meeting_invitation_notification" = "Sie sind zu einer Besprechung eingeladen";
"message_meeting_invitation_updated_notification" = "Besprechung wurde aktualisiert";
"message_reactions_info_all_title" = "Reaktionen";
"network_reachable_again" = "Netzwerk ist nun wieder erreichbar";
"None" = "Kein";

View file

@ -206,6 +206,11 @@
"conversation_dialog_subject_hint" = "Conversation subject";
"conversation_editing_message_title" = "Message being edited";
"conversation_message_edited_label" = "Edited";
"conversation_dialog_delete_chat_message_title" = "Delete this message?";
"conversation_dialog_delete_locally_label" = "For me";
"conversation_dialog_delete_for_everyone_label" = "For everyone";
"conversation_message_content_deleted_label" = "This message has been deleted";
"conversation_message_content_deleted_by_us_label" = "You have deleted this message";
"conversation_end_to_end_encrypted_bottom_sheet_link" = "https://linphone.org/en/features/#security";
"conversation_end_to_end_encrypted_event_title" = "End-to-end encrypted conversation";
"conversation_end_to_end_encrypted_event_subtitle" = "Messages in this conversation are e2e encrypted. Only your correspondent can decrypt them.";
@ -407,9 +412,9 @@
"message_delivery_info_received_title" = "Received";
"message_delivery_info_sent_title" = "Sent";
"message_forwarded_label" = "Forwarded";
"message_meeting_invitation_cancelled_notification" = "📅 Meeting has been cancelled";
"message_meeting_invitation_notification" = "📅 You are invited to a meeting";
"message_meeting_invitation_updated_notification" = "📅 Meeting has been updated";
"message_meeting_invitation_cancelled_notification" = "Meeting has been cancelled";
"message_meeting_invitation_notification" = "You are invited to a meeting";
"message_meeting_invitation_updated_notification" = "Meeting has been updated";
"message_reaction_click_to_remove_label" = "Click to remove";
"message_reactions_info_all_title" = "Reactions";
"network_not_reachable" = "You aren't connected to internet";

View file

@ -206,6 +206,11 @@
"conversation_dialog_subject_hint" = "Nom de la conversation";
"conversation_editing_message_title" = "Modification du message";
"conversation_message_edited_label" = "Modifié";
"conversation_dialog_delete_chat_message_title" = "Supprimer le message ?";
"conversation_dialog_delete_locally_label" = "Pour moi";
"conversation_dialog_delete_for_everyone_label" = "Pour tout le monde";
"conversation_message_content_deleted_label" = "*Le message a été supprimé*";
"conversation_message_content_deleted_by_us_label" = "*Vous avez supprimé le message*";
"conversation_end_to_end_encrypted_bottom_sheet_link" = "https://linphone.org/en/features/#security";
"conversation_end_to_end_encrypted_event_title" = "Conversation chiffrée de bout en bout";
"conversation_end_to_end_encrypted_event_subtitle" = "Les messages de cette conversation sont chiffrés de bout en bout. Seul votre correspondant peut les déchiffrer.";
@ -407,9 +412,9 @@
"message_delivery_info_received_title" = "Reçu";
"message_delivery_info_sent_title" = "Envoyé";
"message_forwarded_label" = "Transféré";
"message_meeting_invitation_cancelled_notification" = "📅 Réunion annulée";
"message_meeting_invitation_notification" = "📅 Invitation à une réunion";
"message_meeting_invitation_updated_notification" = "📅 Réunion mise à jour";
"message_meeting_invitation_cancelled_notification" = "Réunion annulée";
"message_meeting_invitation_notification" = "Invitation à une réunion";
"message_meeting_invitation_updated_notification" = "Réunion mise à jour";
"message_reaction_click_to_remove_label" = "Cliquez pour supprimer";
"message_reactions_info_all_title" = "Réactions";
"network_not_reachable" = "Vous nêtes pas connecté à internet";

View file

@ -456,9 +456,9 @@
"message_delivery_info_read_title" = "Читати";
"message_delivery_info_received_title" = "Отримано";
"message_delivery_info_sent_title" = "Відправлено";
"message_meeting_invitation_cancelled_notification" = "📅 Нараду скасовано";
"message_meeting_invitation_notification" = "📅 Вас запрошено на нараду";
"message_meeting_invitation_updated_notification" = "📅 Нараду оновлено";
"message_meeting_invitation_cancelled_notification" = "Нараду скасовано";
"message_meeting_invitation_notification" = "Вас запрошено на нараду";
"message_meeting_invitation_updated_notification" = "Нараду оновлено";
"message_reactions_info_all_title" = "Реакції";
"network_reachable_again" = "Мережа знову доступна";
"None" = "Жоден";

View file

@ -60,6 +60,7 @@ struct ContentView: View {
@State var isShowDismissPopup = false
@State var isShowSendCancelMeetingNotificationPopup = false
@State var isShowStartCallGroupPopup = false
@State var isShowDeleteMessagePopup = false
@State var isShowSipAddressesPopup = false
@State var isShowSipAddressesPopupType = 0 // 0 to call, 1 to message, 2 to video call
@State var isShowConversationFragment = false
@ -987,6 +988,7 @@ struct ContentView: View {
ConversationFragment(
isShowConversationFragment: $isShowConversationFragment,
isShowStartCallGroupPopup: $isShowStartCallGroupPopup,
isShowDeleteMessagePopup: $isShowDeleteMessagePopup,
isShowEditContactFragment: $isShowEditContactFragment,
isShowEditContactFragmentAddress: $isShowEditContactFragmentAddress,
isShowScheduleMeetingFragment: $isShowScheduleMeetingFragment,
@ -1354,31 +1356,30 @@ struct ContentView: View {
}
}
/*
if isShowStartCallGroupPopup {
if isShowDeleteMessagePopup {
PopupView(
isShowPopup: $isShowStartCallGroupPopup,
title: Text("conversation_info_confirm_start_group_call_dialog_title"),
content: Text("conversation_info_confirm_start_group_call_dialog_message"),
titleFirstButton: Text("dialog_cancel"),
actionFirstButton: { self.isShowStartCallGroupPopup.toggle() },
titleSecondButton: Text("dialog_confirm"),
isShowPopup: $isShowDeleteMessagePopup,
title: Text("conversation_dialog_delete_chat_message_title"),
content: nil,
titleFirstButton: Text("conversation_dialog_delete_for_everyone_label"),
actionFirstButton: {
NotificationCenter.default.post(name: NSNotification.Name("DeleteMessageForEveryone"), object: nil)
self.isShowDeleteMessagePopup.toggle()
},
titleSecondButton: Text("conversation_dialog_delete_locally_label"),
actionSecondButton: {
if sharedMainViewModel.displayedConversation != nil {
sharedMainViewModel.displayedConversation!.createGroupCall()
}
self.isShowStartCallGroupPopup.toggle()
NotificationCenter.default.post(name: NSNotification.Name("DeleteMessageForMe"), object: nil)
self.isShowDeleteMessagePopup.toggle()
},
titleThirdButton: Text("dialog_cancel"),
actionThirdButton: { self.isShowStartCallGroupPopup.toggle() }
actionThirdButton: { self.isShowDeleteMessagePopup.toggle() }
)
.background(.black.opacity(0.65))
.zIndex(3)
.onTapGesture {
self.isShowStartCallGroupPopup.toggle()
self.isShowDeleteMessagePopup.toggle()
}
}
*/
if isShowConversationInfoPopup {
PopupViewWithTextField(

View file

@ -52,7 +52,7 @@ struct ChatBubbleView: View {
HStack {
if eventLogMessage.eventModel.eventLogType == .ConferenceChatMessage {
VStack {
if !eventLogMessage.message.text.isEmpty || !eventLogMessage.message.attachments.isEmpty || eventLogMessage.message.isIcalendar {
if !eventLogMessage.message.text.isEmpty || !eventLogMessage.message.attachments.isEmpty || eventLogMessage.message.isIcalendar || eventLogMessage.message.isRetracted {
HStack(alignment: .top, content: {
if eventLogMessage.message.isOutgoing {
Spacer()
@ -137,6 +137,12 @@ struct ChatBubbleView: View {
.foregroundStyle(Color.grayMain2c700)
.default_text_style(styleSize: 14)
.lineLimit(/*@START_MENU_TOKEN@*/2/*@END_MENU_TOKEN@*/)
} else if eventLogMessage.message.replyMessage!.isRetracted {
Text(eventLogMessage.message.replyMessage!.isOutgoing ? "conversation_message_content_deleted_by_us_label" : "conversation_message_content_deleted_label")
.italic()
.foregroundStyle(Color.grayMain2c500)
.font(.system(size: 14))
.lineLimit(1)
}
}
.padding(.all, 15)
@ -174,6 +180,12 @@ struct ChatBubbleView: View {
if !eventLogMessage.message.text.isEmpty {
DynamicLinkText(text: eventLogMessage.message.text)
} else if eventLogMessage.message.isRetracted {
Text(eventLogMessage.message.isOutgoing ? "conversation_message_content_deleted_by_us_label" : "conversation_message_content_deleted_label")
.italic()
.foregroundStyle(Color.grayMain2c500)
.font(.system(size: 14))
.lineLimit(1)
}
if eventLogMessage.message.isIcalendar && eventLogMessage.message.messageConferenceInfo != nil {

View file

@ -64,6 +64,7 @@ struct ConversationFragment: View {
@Binding var isShowConversationFragment: Bool
@Binding var isShowStartCallGroupPopup: Bool
@Binding var isShowDeleteMessagePopup: Bool
@State private var selectedCategoryIndex = 0
@ -1194,28 +1195,30 @@ struct ConversationFragment: View {
Divider()
}
Button {
let indexMessage = conversationViewModel.conversationMessagesSection[0].rows.firstIndex(where: {$0.message.id == conversationViewModel.selectedMessage!.message.id})
conversationViewModel.selectedMessage = nil
conversationViewModel.replyToMessage(index: indexMessage ?? 0, isMessageTextFocused: Binding(
get: { isMessageTextFocused },
set: { isMessageTextFocused = $0 }
))
} label: {
HStack {
Text("menu_reply_to_chat_message")
.default_text_style(styleSize: 15)
Spacer()
Image("reply")
.resizable()
.frame(width: 20, height: 20, alignment: .leading)
if !conversationViewModel.selectedMessage!.message.isRetracted {
Button {
let indexMessage = conversationViewModel.conversationMessagesSection[0].rows.firstIndex(where: {$0.message.id == conversationViewModel.selectedMessage!.message.id})
conversationViewModel.selectedMessage = nil
conversationViewModel.replyToMessage(index: indexMessage ?? 0, isMessageTextFocused: Binding(
get: { isMessageTextFocused },
set: { isMessageTextFocused = $0 }
))
} label: {
HStack {
Text("menu_reply_to_chat_message")
.default_text_style(styleSize: 15)
Spacer()
Image("reply")
.resizable()
.frame(width: 20, height: 20, alignment: .leading)
}
.padding(.vertical, 5)
.padding(.horizontal, 20)
}
.padding(.vertical, 5)
.padding(.horizontal, 20)
Divider()
}
Divider()
if !conversationViewModel.selectedMessage!.message.text.isEmpty {
Button {
UIPasteboard.general.setValue(
@ -1243,27 +1246,35 @@ struct ConversationFragment: View {
Divider()
}
Button {
withAnimation {
isShowConversationForwardMessageFragment = true
if !conversationViewModel.selectedMessage!.message.isRetracted {
Button {
withAnimation {
isShowConversationForwardMessageFragment = true
}
} label: {
HStack {
Text("menu_forward_chat_message")
.default_text_style(styleSize: 15)
Spacer()
Image("forward")
.resizable()
.frame(width: 20, height: 20, alignment: .leading)
}
.padding(.vertical, 5)
.padding(.horizontal, 20)
}
} label: {
HStack {
Text("menu_forward_chat_message")
.default_text_style(styleSize: 15)
Spacer()
Image("forward")
.resizable()
.frame(width: 20, height: 20, alignment: .leading)
}
.padding(.vertical, 5)
.padding(.horizontal, 20)
Divider()
}
Divider()
Button {
conversationViewModel.deleteMessage()
if conversationViewModel.selectedMessage!.message.isOutgoing
&& !(SharedMainViewModel.shared.displayedConversation?.isReadOnly ?? cachedConversation!.isReadOnly)
&& conversationViewModel.selectedMessage!.message.isRetractable && !conversationViewModel.selectedMessage!.message.isRetracted {
isShowDeleteMessagePopup = true
} else {
conversationViewModel.deleteMessage()
}
} label: {
HStack {
Text("menu_delete_selected_item")
@ -1316,6 +1327,10 @@ struct ConversationFragment: View {
if conversationViewModel.selectedMessage != nil {
conversationViewModel.selectedMessage = nil
}
}.onReceive(NotificationCenter.default.publisher(for: NSNotification.Name("DeleteMessageForMe"))) { _ in
conversationViewModel.deleteMessage()
}.onReceive(NotificationCenter.default.publisher(for: NSNotification.Name("DeleteMessageForEveryone"))) { _ in
conversationViewModel.deleteMessageForEveryone()
}
}

View file

@ -105,14 +105,26 @@ struct ConversationRow: View {
.frame(maxWidth: .infinity, alignment: .leading)
.lineLimit(1)
Text(conversation.lastMessageText)
.foregroundStyle(Color.grayMain2c400)
.if(conversation.unreadMessagesCount > 0) { view in
view.default_text_style_700(styleSize: 14)
}
.default_text_style(styleSize: 14)
.frame(maxWidth: .infinity, alignment: .leading)
.lineLimit(1)
if conversation.lastMessageInItalic {
Text(conversation.lastMessageText)
.italic()
.if(conversation.unreadMessagesCount > 0) { view in
view.bold()
}
.foregroundStyle(Color.grayMain2c400)
.font(.system(size: 14))
.frame(maxWidth: .infinity, alignment: .leading)
.lineLimit(1)
} else {
Text(conversation.lastMessageText)
.foregroundStyle(Color.grayMain2c400)
.if(conversation.unreadMessagesCount > 0) { view in
view.default_text_style_700(styleSize: 14)
}
.default_text_style(styleSize: 14)
.frame(maxWidth: .infinity, alignment: .leading)
.lineLimit(1)
}
Spacer()
}

View file

@ -551,6 +551,12 @@ struct UIList: UIViewRepresentable {
}
func tableView(_ tableView: UITableView, leadingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let eventLogMessage = parent.conversationViewModel.conversationMessagesSection[0].rows[indexPath.row]
guard !eventLogMessage.message.isRetracted && eventLogMessage.eventModel.eventLogType == .ConferenceChatMessage else {
return nil
}
let archiveAction = UIContextualAction(style: .normal, title: "") { _, _, completionHandler in
self.parent.conversationViewModel.replyToMessage(index: indexPath.row, isMessageTextFocused: Binding(
get: { self.parent.isMessageTextFocused },

View file

@ -49,6 +49,7 @@ class ConversationModel: ObservableObject, Identifiable {
@Published var lastMessageText: String
@Published var lastMessageIsOutgoing: Bool
@Published var lastMessageState: Int
@Published var lastMessageInItalic: Bool
@Published var unreadMessagesCount: Int
@Published var avatarModel: ContactAvatarModel
@ -143,6 +144,8 @@ class ConversationModel: ObservableObject, Identifiable {
self.lastMessageIsOutgoing = false
self.lastMessageState = 0
self.lastMessageInItalic = false
self.unreadMessagesCount = chatRoom.unreadMessagesCount
@ -297,6 +300,8 @@ class ConversationModel: ObservableObject, Identifiable {
var lastMessageTextTmp = (fromAddressFriend ?? "")
+ (lastMessage!.contents.first(where: {$0.isText == true})?.utf8Text ?? (lastMessage!.contents.first(where: {$0.isFile == true || $0.isFileTransfer == true})?.name ?? ""))
var lastMessageInItalicTmp = false
if lastMessage!.contents.first != nil && lastMessage!.contents.first!.isIcalendar == true {
if let conferenceInfo = try? Factory.Instance.createConferenceInfoFromIcalendarContent(content: lastMessage!.contents.first!) {
if conferenceInfo.uri != nil {
@ -308,10 +313,18 @@ class ConversationModel: ObservableObject, Identifiable {
} else if conferenceInfo.state == .Cancelled {
lastMessageTextTmp = String(localized: "message_meeting_invitation_cancelled_notification")
}
lastMessageInItalicTmp = true
}
}
}
if lastMessage!.isRetracted {
lastMessageTextTmp += lastMessage!.isOutgoing ? String(localized: "conversation_message_content_deleted_by_us_label") : String(localized: "conversation_message_content_deleted_label")
lastMessageInItalicTmp = true
}
let lastMessageIsOutgoingTmp = lastMessage?.isOutgoing ?? false
let lastUpdateTimeTmp = lastMessage?.time ?? chatRoom.lastUpdateTime
@ -326,6 +339,8 @@ class ConversationModel: ObservableObject, Identifiable {
self.lastUpdateTime = lastUpdateTimeTmp
self.lastMessageState = lastMessageStateTmp
self.lastMessageInItalic = lastMessageInItalicTmp
}
getUnreadMessagesCount()

View file

@ -69,7 +69,9 @@ public struct Message: Identifiable, Hashable {
public var createdAt: Date
public var isOutgoing: Bool
public var isEditable: Bool
public var isRetractable: Bool
public var isEdited: Bool
public var isRetracted: Bool
public var dateReceived: time_t
public var address: String
@ -97,7 +99,9 @@ public struct Message: Identifiable, Hashable {
createdAt: Date = Date(),
isOutgoing: Bool,
isEditable: Bool,
isRetractable: Bool,
isEdited: Bool,
isRetracted: Bool,
dateReceived: time_t,
address: String,
isFirstMessage: Bool = false,
@ -121,7 +125,9 @@ public struct Message: Identifiable, Hashable {
self.createdAt = createdAt
self.isOutgoing = isOutgoing
self.isEditable = isEditable
self.isRetractable = isRetractable
self.isEdited = isEdited
self.isRetracted = isRetracted
self.dateReceived = dateReceived
self.isFirstMessage = isFirstMessage
self.address = address
@ -170,7 +176,9 @@ public struct Message: Identifiable, Hashable {
createdAt: draft.createdAt,
isOutgoing: draft.isOutgoing,
isEditable: draft.isEditable,
isRetractable: draft.isRetractable,
isEdited: draft.isEdited,
isRetracted: draft.isRetracted,
dateReceived: draft.dateReceived,
address: draft.address,
isFirstMessage: draft.isFirstMessage,
@ -192,7 +200,7 @@ extension Message {
extension Message: Equatable {
public static func == (lhs: Message, rhs: Message) -> Bool {
lhs.id == rhs.id && lhs.status == rhs.status && lhs.isEdited == rhs.isEdited && lhs.isFirstMessage == rhs.isFirstMessage && lhs.text == rhs.text && lhs.attachments == rhs.attachments && lhs.replyMessage?.text == rhs.replyMessage?.text && lhs.ownReaction == rhs.ownReaction && lhs.reactions == rhs.reactions && lhs.ephemeralExpireTime == rhs.ephemeralExpireTime
lhs.id == rhs.id && lhs.status == rhs.status && lhs.isEdited == rhs.isEdited && lhs.isRetracted == rhs.isRetracted && lhs.isFirstMessage == rhs.isFirstMessage && lhs.text == rhs.text && lhs.attachments == rhs.attachments && lhs.replyMessage?.text == rhs.replyMessage?.text && lhs.replyMessage?.isRetracted == rhs.replyMessage?.isRetracted && lhs.ownReaction == rhs.ownReaction && lhs.reactions == rhs.reactions && lhs.ephemeralExpireTime == rhs.ephemeralExpireTime
}
}
@ -220,7 +228,9 @@ public struct ReplyMessage: Codable, Identifiable, Hashable {
public var text: String
public var isOutgoing: Bool
public var isEditable: Bool
public var isRetractable: Bool
public var isEdited: Bool
public var isRetracted: Bool
public var dateReceived: time_t
public var attachmentsNames: String
public var attachments: [Attachment]
@ -232,7 +242,9 @@ public struct ReplyMessage: Codable, Identifiable, Hashable {
text: String = "",
isOutgoing: Bool,
isEditable: Bool,
isRetractable: Bool,
isEdited: Bool,
isRetracted: Bool,
dateReceived: time_t,
attachmentsNames: String = "",
attachments: [Attachment] = [],
@ -244,7 +256,9 @@ public struct ReplyMessage: Codable, Identifiable, Hashable {
self.text = text
self.isOutgoing = isOutgoing
self.isEditable = isEditable
self.isRetractable = isRetractable
self.isEdited = isEdited
self.isRetracted = isRetracted
self.dateReceived = dateReceived
self.attachmentsNames = attachmentsNames
self.attachments = attachments
@ -252,14 +266,14 @@ public struct ReplyMessage: Codable, Identifiable, Hashable {
}
func toMessage() -> Message {
Message(id: id, isOutgoing: isOutgoing, isEditable: isEditable, isEdited: isEdited, dateReceived: dateReceived, address: address, isFirstMessage: isFirstMessage, text: text, attachments: attachments, recording: recording)
Message(id: id, isOutgoing: isOutgoing, isEditable: isEditable, isRetractable: isRetractable, isEdited: isEdited, isRetracted: isRetracted, 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, isEditable: isEditable, isEdited: isEdited, dateReceived: dateReceived, attachments: attachments, recording: recording)
ReplyMessage(id: id, address: address, isFirstMessage: isFirstMessage, text: text, isOutgoing: isOutgoing, isEditable: isEditable, isRetractable: isRetractable, isEdited: isEdited, isRetracted: isRetracted, dateReceived: dateReceived, attachments: attachments, recording: recording)
}
}
@ -267,7 +281,9 @@ public struct DraftMessage {
public var id: String?
public let isOutgoing: Bool
public let isEditable: Bool
public let isRetractable: Bool
public let isEdited: Bool
public let isRetracted: Bool
public var dateReceived: time_t
public let address: String
public let isFirstMessage: Bool
@ -282,7 +298,9 @@ public struct DraftMessage {
public init(id: String? = nil,
isOutgoing: Bool,
isEditable: Bool,
isRetractable: Bool,
isEdited: Bool,
isRetracted: Bool,
dateReceived: time_t,
address: String,
isFirstMessage: Bool,
@ -297,7 +315,9 @@ public struct DraftMessage {
self.id = id
self.isOutgoing = isOutgoing
self.isEditable = isEditable
self.isRetractable = isRetractable
self.isEdited = isEdited
self.isRetracted = isRetracted
self.dateReceived = dateReceived
self.address = address
self.isFirstMessage = isFirstMessage

View file

@ -175,6 +175,10 @@ class ConversationViewModel: ObservableObject {
}, onMessageContentEdited: {(chatRoom: ChatRoom, message: ChatMessage) in
let indexMessage = self.conversationMessagesSection[0].rows.firstIndex(where: {$0.eventModel.eventLogId == message.messageId})
if let displayedConversation = self.sharedMainViewModel.displayedConversation {
displayedConversation.getContentTextMessage(chatRoom: displayedConversation.chatRoom)
}
var attachmentNameList: String = ""
var attachmentList: [Attachment] = []
var contentText = ""
@ -290,7 +294,28 @@ class ConversationViewModel: ObservableObject {
}
}
}, onMessageRetracted: {(chatRoom: ChatRoom, message: ChatMessage) in
// TODO
let indexMessage = self.conversationMessagesSection[0].rows.firstIndex(where: {$0.eventModel.eventLogId == message.messageId})
let indexReplyMessage = self.conversationMessagesSection[0].rows.firstIndex(where: {$0.message.replyMessage?.id == message.messageId})
if let displayedConversation = self.sharedMainViewModel.displayedConversation {
displayedConversation.getContentTextMessage(chatRoom: displayedConversation.chatRoom)
}
DispatchQueue.main.async {
if indexMessage != nil {
self.conversationMessagesSection[0].rows[indexMessage!].message.text = ""
self.conversationMessagesSection[0].rows[indexMessage!].message.isRetracted = true
self.conversationMessagesSection[0].rows[indexMessage!].message.attachments = []
self.conversationMessagesSection[0].rows[indexMessage!].message.attachmentsNames = ""
}
if indexReplyMessage != nil {
self.conversationMessagesSection[0].rows[indexReplyMessage!].message.replyMessage?.text = ""
self.conversationMessagesSection[0].rows[indexReplyMessage!].message.replyMessage?.isRetracted = true
self.conversationMessagesSection[0].rows[indexReplyMessage!].message.replyMessage?.attachments = []
self.conversationMessagesSection[0].rows[indexReplyMessage!].message.replyMessage?.attachmentsNames = ""
}
}
})
self.chatRoomDelegateHolder = ChatRoomDelegateHolder(chatroom: chatRoom, delegate: chatRoomDelegate)
@ -666,7 +691,9 @@ class ConversationViewModel: ObservableObject {
status: nil,
isOutgoing: false,
isEditable: false,
isRetractable: false,
isEdited: false,
isRetracted: false,
dateReceived: 0,
address: "",
isFirstMessage: false,
@ -827,6 +854,8 @@ class ConversationViewModel: ObservableObject {
let contentReplyText = chatMessage.replyMessage?.utf8Text ?? ""
let isReplyRetracted = chatMessage.replyMessage?.isRetracted ?? false
var attachmentNameReplyList: String = ""
chatMessage.replyMessage?.contents.forEach { content in
@ -844,9 +873,11 @@ class ConversationViewModel: ObservableObject {
address: addressReplyCleaned?.asStringUriOnly() ?? "",
isFirstMessage: false,
text: contentReplyText,
isOutgoing: false,
isOutgoing: chatMessage.replyMessage!.isOutgoing,
isEditable: false,
isRetractable: false,
isEdited: false,
isRetracted: isReplyRetracted,
dateReceived: 0,
attachmentsNames: attachmentNameReplyList,
attachments: []
@ -861,7 +892,9 @@ class ConversationViewModel: ObservableObject {
status: statusTmp,
isOutgoing: chatMessage.isOutgoing,
isEditable: chatMessage.isOutgoing ? chatMessage.isEditable : false,
isRetractable: chatMessage.isOutgoing ? chatMessage.isRetractable : false,
isEdited: chatMessage.isEdited,
isRetracted: chatMessage.isRetracted,
dateReceived: chatMessage.time,
address: addressCleaned?.asStringUriOnly() ?? "",
isFirstMessage: isFirstMessageTmp,
@ -916,7 +949,9 @@ class ConversationViewModel: ObservableObject {
status: nil,
isOutgoing: false,
isEditable: false,
isRetractable: false,
isEdited: false,
isRetracted: false,
dateReceived: 0,
address: "",
isFirstMessage: false,
@ -1076,6 +1111,8 @@ class ConversationViewModel: ObservableObject {
let contentReplyText = chatMessage.replyMessage?.utf8Text ?? ""
let isReplyRetracted = chatMessage.replyMessage?.isRetracted ?? false
var attachmentNameReplyList: String = ""
chatMessage.replyMessage?.contents.forEach { content in
@ -1093,9 +1130,11 @@ class ConversationViewModel: ObservableObject {
address: addressReplyCleaned?.asStringUriOnly() ?? "",
isFirstMessage: false,
text: contentReplyText,
isOutgoing: false,
isOutgoing: chatMessage.replyMessage!.isOutgoing,
isEditable: false,
isRetractable: false,
isEdited: false,
isRetracted: isReplyRetracted,
dateReceived: 0,
attachmentsNames: attachmentNameReplyList,
attachments: []
@ -1110,7 +1149,9 @@ class ConversationViewModel: ObservableObject {
status: statusTmp,
isOutgoing: chatMessage.isOutgoing,
isEditable: chatMessage.isOutgoing ? chatMessage.isEditable : false,
isRetractable: chatMessage.isOutgoing ? chatMessage.isRetractable : false,
isEdited: chatMessage.isEdited,
isRetracted: chatMessage.isRetracted,
dateReceived: chatMessage.time,
address: addressCleaned?.asStringUriOnly() ?? "",
isFirstMessage: isFirstMessageTmp,
@ -1182,7 +1223,9 @@ class ConversationViewModel: ObservableObject {
status: nil,
isOutgoing: false,
isEditable: false,
isRetractable: false,
isEdited: false,
isRetracted: false,
dateReceived: 0,
address: "",
isFirstMessage: false,
@ -1356,6 +1399,8 @@ class ConversationViewModel: ObservableObject {
let contentReplyText = chatMessage.replyMessage?.utf8Text ?? ""
let isReplyRetracted = chatMessage.replyMessage?.isRetracted ?? false
var attachmentNameReplyList: String = ""
chatMessage.replyMessage?.contents.forEach { content in
@ -1373,9 +1418,11 @@ class ConversationViewModel: ObservableObject {
address: addressReplyCleaned != nil ? addressReplyCleaned!.asStringUriOnly() : "",
isFirstMessage: false,
text: contentReplyText,
isOutgoing: false,
isOutgoing: chatMessage.replyMessage!.isOutgoing,
isEditable: false,
isRetractable: false,
isEdited: false,
isRetracted: isReplyRetracted,
dateReceived: 0,
attachmentsNames: attachmentNameReplyList,
attachments: []
@ -1391,7 +1438,9 @@ class ConversationViewModel: ObservableObject {
status: statusTmp,
isOutgoing: chatMessage.isOutgoing,
isEditable: chatMessage.isOutgoing ? chatMessage.isEditable : false,
isRetractable: chatMessage.isOutgoing ? chatMessage.isRetractable : false,
isEdited: chatMessage.isEdited,
isRetracted: chatMessage.isRetracted,
dateReceived: chatMessage.time,
address: addressCleaned != nil ? addressCleaned!.asStringUriOnly() : "",
isFirstMessage: isFirstMessageTmp,
@ -1592,6 +1641,8 @@ class ConversationViewModel: ObservableObject {
let contentReplyText = chatMessage.replyMessage?.utf8Text ?? ""
let isReplyRetracted = chatMessage.replyMessage?.isRetracted ?? false
var attachmentNameReplyList: String = ""
chatMessage.replyMessage?.contents.forEach { content in
@ -1609,9 +1660,11 @@ class ConversationViewModel: ObservableObject {
address: addressReplyCleaned != nil ? addressReplyCleaned!.asStringUriOnly() : "",
isFirstMessage: false,
text: contentReplyText,
isOutgoing: false,
isOutgoing: chatMessage.replyMessage!.isOutgoing,
isEditable: false,
isRetractable: false,
isEdited: false,
isRetracted: isReplyRetracted,
dateReceived: 0,
attachmentsNames: attachmentNameReplyList,
attachments: []
@ -1627,7 +1680,9 @@ class ConversationViewModel: ObservableObject {
status: statusTmp,
isOutgoing: chatMessage.isOutgoing,
isEditable: chatMessage.isOutgoing ? chatMessage.isEditable : false,
isRetractable: chatMessage.isOutgoing ? chatMessage.isRetractable : false,
isEdited: chatMessage.isEdited,
isRetracted: chatMessage.isRetracted,
dateReceived: chatMessage.time,
address: addressCleaned != nil ? addressCleaned!.asStringUriOnly() : "",
isFirstMessage: isFirstMessageTmp,
@ -1670,7 +1725,9 @@ class ConversationViewModel: ObservableObject {
status: nil,
isOutgoing: false,
isEditable: false,
isRetractable: false,
isEdited: false,
isRetracted: false,
dateReceived: 0,
address: "",
isFirstMessage: false,
@ -1783,7 +1840,9 @@ class ConversationViewModel: ObservableObject {
status: nil,
isOutgoing: false,
isEditable: false,
isRetractable: false,
isEdited: false,
isRetracted: false,
dateReceived: 0,
address: "",
isFirstMessage: false,
@ -1943,6 +2002,8 @@ class ConversationViewModel: ObservableObject {
let contentReplyText = chatMessage.replyMessage?.utf8Text ?? ""
let isReplyRetracted = chatMessage.replyMessage?.isRetracted ?? false
var attachmentNameReplyList: String = ""
chatMessage.replyMessage?.contents.forEach { content in
@ -1960,9 +2021,11 @@ class ConversationViewModel: ObservableObject {
address: addressReplyCleaned?.asStringUriOnly() ?? "",
isFirstMessage: false,
text: contentReplyText,
isOutgoing: false,
isOutgoing: chatMessage.replyMessage!.isOutgoing,
isEditable: false,
isRetractable: false,
isEdited: false,
isRetracted: isReplyRetracted,
dateReceived: 0,
attachmentsNames: attachmentNameReplyList,
attachments: []
@ -1977,7 +2040,9 @@ class ConversationViewModel: ObservableObject {
status: statusTmp,
isOutgoing: chatMessage.isOutgoing,
isEditable: chatMessage.isOutgoing ? chatMessage.isEditable : false,
isRetractable: chatMessage.isOutgoing ? chatMessage.isRetractable : false,
isEdited: chatMessage.isEdited,
isRetracted: chatMessage.isRetracted,
dateReceived: chatMessage.time,
address: addressCleaned?.asStringUriOnly() ?? "",
isFirstMessage: isFirstMessageTmp,
@ -2845,17 +2910,39 @@ class ConversationViewModel: ObservableObject {
if let displayedConversation = self.sharedMainViewModel.displayedConversation,
let selectedMessage = self.selectedMessage,
let chatMessage = selectedMessage.eventModel.eventLog.chatMessage {
let indexReplyMessage = self.conversationMessagesSection[0].rows.firstIndex(where: {$0.message.replyMessage?.id == chatMessage.messageId})
displayedConversation.chatRoom.deleteMessage(message: chatMessage)
displayedConversation.getContentTextMessage(chatRoom: displayedConversation.chatRoom)
DispatchQueue.main.async {
if let sectionIndex = self.conversationMessagesSection.firstIndex(where: { $0.chatRoomID == displayedConversation.id }),
let rowIndex = self.conversationMessagesSection[sectionIndex].rows.firstIndex(of: selectedMessage) {
self.conversationMessagesSection[sectionIndex].rows.remove(at: rowIndex)
if indexReplyMessage != nil {
self.conversationMessagesSection[0].rows[indexReplyMessage!].message.replyMessage = nil
}
}
self.selectedMessage = nil
}
}
}
}
func deleteMessageForEveryone(){
coreContext.doOnCoreQueue { _ in
if let displayedConversation = self.sharedMainViewModel.displayedConversation,
let selectedMessage = self.selectedMessage,
let chatMessage = selectedMessage.eventModel.eventLog.chatMessage {
displayedConversation.chatRoom.retractMessage(message: chatMessage)
DispatchQueue.main.async {
self.selectedMessage = nil
}
}
}
}
}
// swiftlint:enable line_length
// swiftlint:enable type_body_length

View file

@ -89,7 +89,11 @@ class ConversationsListViewModel: ObservableObject {
fromAddressFriend = nil
}
let lastMessageTextTmp = (fromAddressFriend ?? "") + (lastMessage.contents.first(where: { $0.isText })?.utf8Text ?? (lastMessage.contents.first(where: { $0.isFile || $0.isFileTransfer })?.name ?? ""))
var lastMessageTextTmp = (fromAddressFriend ?? "") + (lastMessage.contents.first(where: { $0.isText })?.utf8Text ?? (lastMessage.contents.first(where: { $0.isFile || $0.isFileTransfer })?.name ?? ""))
if lastMessage.isRetracted {
lastMessageTextTmp += lastMessage.isOutgoing ? String(localized: "conversation_message_content_deleted_by_us_label") : String(localized: "conversation_message_content_deleted_label")
}
if let index = self.conversationsList.firstIndex(where: { $0.chatRoom === conversationModel.chatRoom }) {
DispatchQueue.main.async {
@ -148,7 +152,11 @@ class ConversationsListViewModel: ObservableObject {
fromAddressFriend = nil
}
let lastMessageTextTmp = (fromAddressFriend ?? "") + (lastMessage.contents.first(where: { $0.isText })?.utf8Text ?? (lastMessage.contents.first(where: { $0.isFile || $0.isFileTransfer })?.name ?? ""))
var lastMessageTextTmp = (fromAddressFriend ?? "") + (lastMessage.contents.first(where: { $0.isText })?.utf8Text ?? (lastMessage.contents.first(where: { $0.isFile || $0.isFileTransfer })?.name ?? ""))
if lastMessage.isRetracted {
lastMessageTextTmp += lastMessage.isOutgoing ? String(localized: "conversation_message_content_deleted_by_us_label") : String(localized: "conversation_message_content_deleted_label")
}
if let index = self.conversationsList.firstIndex(where: { $0.chatRoom === conversationModel.chatRoom }) {
DispatchQueue.main.async {