mirror of
https://gitlab.linphone.org/BC/public/linphone-iphone.git
synced 2026-01-17 02:58:07 +00:00
Add message editing feature
This commit is contained in:
parent
fa1f8386b4
commit
7972fd7c1f
6 changed files with 343 additions and 28 deletions
|
|
@ -204,6 +204,8 @@
|
|||
"conversation_dialog_edit_subject" = "Edit conversation subject";
|
||||
"conversation_dialog_set_subject" = "Set conversation subject";
|
||||
"conversation_dialog_subject_hint" = "Conversation subject";
|
||||
"conversation_editing_message_title" = "Message being edited";
|
||||
"conversation_message_edited_label" = "Edited";
|
||||
"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.";
|
||||
|
|
@ -394,6 +396,7 @@
|
|||
"menu_delete_selected_item" = "Delete";
|
||||
"menu_forward_chat_message" = "Forward";
|
||||
"menu_invite" = "Invite";
|
||||
"menu_edit_chat_message" = "Edit";
|
||||
"menu_reply_to_chat_message" = "Reply";
|
||||
"menu_resend_chat_message" = "Re-send";
|
||||
"menu_see_existing_contact" = "See contact";
|
||||
|
|
|
|||
|
|
@ -204,6 +204,8 @@
|
|||
"conversation_dialog_edit_subject" = "Renommer la conversation";
|
||||
"conversation_dialog_set_subject" = "Nommer la conversation";
|
||||
"conversation_dialog_subject_hint" = "Nom de la conversation";
|
||||
"conversation_editing_message_title" = "Modification du message";
|
||||
"conversation_message_edited_label" = "Modifié";
|
||||
"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.";
|
||||
|
|
@ -394,6 +396,7 @@
|
|||
"menu_delete_selected_item" = "Supprimer";
|
||||
"menu_forward_chat_message" = "Transférer";
|
||||
"menu_invite" = "Inviter";
|
||||
"menu_edit_chat_message" = "Modifier";
|
||||
"menu_reply_to_chat_message" = "Répondre";
|
||||
"menu_resend_chat_message" = "Ré-envoyer";
|
||||
"menu_see_existing_contact" = "Voir le contact";
|
||||
|
|
|
|||
|
|
@ -325,6 +325,14 @@ struct ChatBubbleView: View {
|
|||
.padding(.top, 1)
|
||||
}
|
||||
|
||||
if eventLogMessage.message.isEdited && eventLogMessage.message.isOutgoing {
|
||||
Text("conversation_message_edited_label")
|
||||
.foregroundStyle(Color.grayMain2c500)
|
||||
.default_text_style_300(styleSize: 12)
|
||||
.padding(.top, 1)
|
||||
.padding(.trailing, -4)
|
||||
}
|
||||
|
||||
Text(conversationViewModel.getMessageTime(startDate: eventLogMessage.message.dateReceived))
|
||||
.foregroundStyle(Color.grayMain2c500)
|
||||
.default_text_style_300(styleSize: 12)
|
||||
|
|
@ -349,6 +357,14 @@ struct ChatBubbleView: View {
|
|||
}
|
||||
}
|
||||
|
||||
if eventLogMessage.message.isEdited && !eventLogMessage.message.isOutgoing {
|
||||
Text("conversation_message_edited_label")
|
||||
.foregroundStyle(Color.grayMain2c500)
|
||||
.default_text_style_300(styleSize: 12)
|
||||
.padding(.top, 1)
|
||||
.padding(.trailing, -4)
|
||||
}
|
||||
|
||||
if eventLogMessage.message.isEphemeral && !eventLogMessage.message.isOutgoing {
|
||||
Image("clock-countdown")
|
||||
.renderingMode(.template)
|
||||
|
|
|
|||
|
|
@ -620,6 +620,43 @@ struct ConversationFragment: View {
|
|||
}
|
||||
}
|
||||
.transition(.move(edge: .bottom))
|
||||
} else if conversationViewModel.messageToEdit != nil {
|
||||
ZStack(alignment: .top) {
|
||||
HStack {
|
||||
VStack {
|
||||
Text("conversation_editing_message_title")
|
||||
.default_text_style_300(styleSize: 15)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.padding(.bottom, 1)
|
||||
.lineLimit(1)
|
||||
|
||||
Text("\(conversationViewModel.messageToEdit!.message.text)")
|
||||
.default_text_style_300(styleSize: 15)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.lineLimit(1)
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding(.all, 20)
|
||||
.background(Color.gray100)
|
||||
|
||||
HStack {
|
||||
Spacer()
|
||||
|
||||
Button(action: {
|
||||
messageText = ""
|
||||
withAnimation {
|
||||
conversationViewModel.messageToEdit = nil
|
||||
}
|
||||
}, label: {
|
||||
Image("x")
|
||||
.resizable()
|
||||
.frame(width: 30, height: 30, alignment: .leading)
|
||||
.padding(.all, 10)
|
||||
})
|
||||
}
|
||||
}
|
||||
.transition(.move(edge: .bottom))
|
||||
}
|
||||
|
||||
if !conversationViewModel.mediasToSend.isEmpty || mediasIsLoading {
|
||||
|
|
@ -879,43 +916,66 @@ struct ConversationFragment: View {
|
|||
}
|
||||
}
|
||||
|
||||
if messageText.isEmpty && conversationViewModel.mediasToSend.isEmpty {
|
||||
Button {
|
||||
voiceRecordingInProgress = true
|
||||
} label: {
|
||||
Image("microphone")
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.foregroundStyle(Color.grayMain2c500)
|
||||
.frame(width: 28, height: 28, alignment: .leading)
|
||||
.padding(.all, 6)
|
||||
.padding(.top, 4)
|
||||
if conversationViewModel.messageToEdit == nil {
|
||||
if messageText.isEmpty && conversationViewModel.mediasToSend.isEmpty {
|
||||
Button {
|
||||
voiceRecordingInProgress = true
|
||||
} label: {
|
||||
Image("microphone")
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.foregroundStyle(Color.grayMain2c500)
|
||||
.frame(width: 28, height: 28, alignment: .leading)
|
||||
.padding(.all, 6)
|
||||
.padding(.top, 4)
|
||||
}
|
||||
} else {
|
||||
Button {
|
||||
if conversationViewModel.displayedConversationHistorySize > 1 {
|
||||
NotificationCenter.default.post(name: .onScrollToBottom, object: nil)
|
||||
}
|
||||
|
||||
let messageTextTmp = self.messageText
|
||||
messageText = " "
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) {
|
||||
messageText = ""
|
||||
isMessageTextFocused = true
|
||||
|
||||
conversationViewModel.sendMessage(messageText: messageTextTmp)
|
||||
}
|
||||
} label: {
|
||||
Image("paper-plane-tilt")
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.foregroundStyle(Color.orangeMain500)
|
||||
.frame(width: 28, height: 28, alignment: .leading)
|
||||
.padding(.all, 6)
|
||||
.padding(.top, 4)
|
||||
.rotationEffect(.degrees(45))
|
||||
}
|
||||
.padding(.trailing, 4)
|
||||
}
|
||||
} else {
|
||||
Button {
|
||||
if conversationViewModel.displayedConversationHistorySize > 1 {
|
||||
NotificationCenter.default.post(name: .onScrollToBottom, object: nil)
|
||||
}
|
||||
|
||||
let messageTextTmp = self.messageText
|
||||
messageText = " "
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) {
|
||||
messageText = ""
|
||||
isMessageTextFocused = true
|
||||
|
||||
conversationViewModel.sendMessage(messageText: messageTextTmp)
|
||||
}
|
||||
messageText = " "
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) {
|
||||
messageText = ""
|
||||
isMessageTextFocused = true
|
||||
|
||||
conversationViewModel.sendMessage(messageText: messageTextTmp)
|
||||
}
|
||||
} label: {
|
||||
Image("paper-plane-tilt")
|
||||
Image("pencil-simple")
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.foregroundStyle(Color.orangeMain500)
|
||||
.foregroundStyle(messageText.isEmpty ? Color.gray300 : Color.orangeMain500)
|
||||
.frame(width: 28, height: 28, alignment: .leading)
|
||||
.padding(.all, 6)
|
||||
.padding(.top, 4)
|
||||
.rotationEffect(.degrees(45))
|
||||
}
|
||||
.padding(.trailing, 4)
|
||||
.disabled(messageText.isEmpty)
|
||||
}
|
||||
}
|
||||
.padding(.leading, 15)
|
||||
|
|
@ -1096,6 +1156,43 @@ struct ConversationFragment: View {
|
|||
|
||||
Divider()
|
||||
}
|
||||
|
||||
if conversationViewModel.selectedMessage!.message.isOutgoing
|
||||
&& !(SharedMainViewModel.shared.displayedConversation?.isReadOnly ?? cachedConversation!.isReadOnly)
|
||||
&& conversationViewModel.selectedMessage!.message.isEditable {
|
||||
Button {
|
||||
if let chatMessage = conversationViewModel.selectedMessage {
|
||||
if voiceRecordingInProgress {
|
||||
voiceRecordingInProgress = false
|
||||
}
|
||||
|
||||
messageText = chatMessage.message.text
|
||||
conversationViewModel.selectedMessage = nil
|
||||
conversationViewModel.editMessage(
|
||||
chatMessage: chatMessage,
|
||||
isMessageTextFocused: Binding(
|
||||
get: { isMessageTextFocused },
|
||||
set: { isMessageTextFocused = $0 }
|
||||
)
|
||||
)
|
||||
}
|
||||
} label: {
|
||||
HStack {
|
||||
Text("menu_edit_chat_message")
|
||||
.default_text_style(styleSize: 15)
|
||||
Spacer()
|
||||
Image("pencil-simple")
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.foregroundStyle(Color.grayMain2c600)
|
||||
.frame(width: 20, height: 20, alignment: .leading)
|
||||
}
|
||||
.padding(.vertical, 5)
|
||||
.padding(.horizontal, 20)
|
||||
}
|
||||
|
||||
Divider()
|
||||
}
|
||||
|
||||
Button {
|
||||
let indexMessage = conversationViewModel.conversationMessagesSection[0].rows.firstIndex(where: {$0.message.id == conversationViewModel.selectedMessage!.message.id})
|
||||
|
|
@ -1211,6 +1308,9 @@ struct ConversationFragment: View {
|
|||
}
|
||||
.onAppear {
|
||||
touchFeedback()
|
||||
if isMessageTextFocused {
|
||||
isMessageTextFocused = false
|
||||
}
|
||||
}
|
||||
.onDisappear {
|
||||
if conversationViewModel.selectedMessage != nil {
|
||||
|
|
|
|||
|
|
@ -68,6 +68,8 @@ public struct Message: Identifiable, Hashable {
|
|||
public var status: Status?
|
||||
public var createdAt: Date
|
||||
public var isOutgoing: Bool
|
||||
public var isEditable: Bool
|
||||
public var isEdited: Bool
|
||||
public var dateReceived: time_t
|
||||
|
||||
public var address: String
|
||||
|
|
@ -94,6 +96,8 @@ public struct Message: Identifiable, Hashable {
|
|||
status: Status? = nil,
|
||||
createdAt: Date = Date(),
|
||||
isOutgoing: Bool,
|
||||
isEditable: Bool,
|
||||
isEdited: Bool,
|
||||
dateReceived: time_t,
|
||||
address: String,
|
||||
isFirstMessage: Bool = false,
|
||||
|
|
@ -116,6 +120,8 @@ public struct Message: Identifiable, Hashable {
|
|||
self.status = status
|
||||
self.createdAt = createdAt
|
||||
self.isOutgoing = isOutgoing
|
||||
self.isEditable = isEditable
|
||||
self.isEdited = isEdited
|
||||
self.dateReceived = dateReceived
|
||||
self.isFirstMessage = isFirstMessage
|
||||
self.address = address
|
||||
|
|
@ -163,6 +169,8 @@ public struct Message: Identifiable, Hashable {
|
|||
status: status,
|
||||
createdAt: draft.createdAt,
|
||||
isOutgoing: draft.isOutgoing,
|
||||
isEditable: draft.isEditable,
|
||||
isEdited: draft.isEdited,
|
||||
dateReceived: draft.dateReceived,
|
||||
address: draft.address,
|
||||
isFirstMessage: draft.isFirstMessage,
|
||||
|
|
@ -184,7 +192,7 @@ extension Message {
|
|||
|
||||
extension Message: Equatable {
|
||||
public static func == (lhs: Message, rhs: Message) -> Bool {
|
||||
lhs.id == rhs.id && lhs.status == rhs.status && lhs.isFirstMessage == rhs.isFirstMessage && lhs.ownReaction == rhs.ownReaction && lhs.reactions == rhs.reactions && lhs.ephemeralExpireTime == rhs.ephemeralExpireTime && lhs.attachments == rhs.attachments
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -211,6 +219,8 @@ public struct ReplyMessage: Codable, Identifiable, Hashable {
|
|||
public var isFirstMessage: Bool
|
||||
public var text: String
|
||||
public var isOutgoing: Bool
|
||||
public var isEditable: Bool
|
||||
public var isEdited: Bool
|
||||
public var dateReceived: time_t
|
||||
public var attachmentsNames: String
|
||||
public var attachments: [Attachment]
|
||||
|
|
@ -221,6 +231,8 @@ public struct ReplyMessage: Codable, Identifiable, Hashable {
|
|||
isFirstMessage: Bool = false,
|
||||
text: String = "",
|
||||
isOutgoing: Bool,
|
||||
isEditable: Bool,
|
||||
isEdited: Bool,
|
||||
dateReceived: time_t,
|
||||
attachmentsNames: String = "",
|
||||
attachments: [Attachment] = [],
|
||||
|
|
@ -231,6 +243,8 @@ public struct ReplyMessage: Codable, Identifiable, Hashable {
|
|||
self.isFirstMessage = isFirstMessage
|
||||
self.text = text
|
||||
self.isOutgoing = isOutgoing
|
||||
self.isEditable = isEditable
|
||||
self.isEdited = isEdited
|
||||
self.dateReceived = dateReceived
|
||||
self.attachmentsNames = attachmentsNames
|
||||
self.attachments = attachments
|
||||
|
|
@ -238,20 +252,22 @@ public struct ReplyMessage: Codable, Identifiable, Hashable {
|
|||
}
|
||||
|
||||
func toMessage() -> Message {
|
||||
Message(id: id, isOutgoing: isOutgoing, dateReceived: dateReceived, address: address, isFirstMessage: isFirstMessage, text: text, attachments: attachments, recording: recording)
|
||||
Message(id: id, isOutgoing: isOutgoing, isEditable: isEditable, isEdited: isEdited, 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, dateReceived: dateReceived, attachments: attachments, recording: recording)
|
||||
ReplyMessage(id: id, address: address, isFirstMessage: isFirstMessage, text: text, isOutgoing: isOutgoing, isEditable: isEditable, isEdited: isEdited, dateReceived: dateReceived, attachments: attachments, recording: recording)
|
||||
}
|
||||
}
|
||||
|
||||
public struct DraftMessage {
|
||||
public var id: String?
|
||||
public let isOutgoing: Bool
|
||||
public let isEditable: Bool
|
||||
public let isEdited: Bool
|
||||
public var dateReceived: time_t
|
||||
public let address: String
|
||||
public let isFirstMessage: Bool
|
||||
|
|
@ -265,6 +281,8 @@ public struct DraftMessage {
|
|||
|
||||
public init(id: String? = nil,
|
||||
isOutgoing: Bool,
|
||||
isEditable: Bool,
|
||||
isEdited: Bool,
|
||||
dateReceived: time_t,
|
||||
address: String,
|
||||
isFirstMessage: Bool,
|
||||
|
|
@ -278,6 +296,8 @@ public struct DraftMessage {
|
|||
) {
|
||||
self.id = id
|
||||
self.isOutgoing = isOutgoing
|
||||
self.isEditable = isEditable
|
||||
self.isEdited = isEdited
|
||||
self.dateReceived = dateReceived
|
||||
self.address = address
|
||||
self.isFirstMessage = isFirstMessage
|
||||
|
|
|
|||
|
|
@ -93,6 +93,7 @@ class ConversationViewModel: ObservableObject {
|
|||
@Published var selectedMessageToPlayVoiceRecording: EventLogMessage?
|
||||
@Published var selectedMessage: EventLogMessage?
|
||||
@Published var messageToReply: EventLogMessage?
|
||||
@Published var messageToEdit: EventLogMessage?
|
||||
|
||||
@Published var sheetCategories: [SheetCategory] = []
|
||||
|
||||
|
|
@ -171,7 +172,127 @@ class ConversationViewModel: ObservableObject {
|
|||
self.getEventMessage(eventLog: eventLog)
|
||||
}, onEphemeralMessageDeleted: {(_: ChatRoom, eventLog: EventLog) in
|
||||
self.removeMessage(eventLog)
|
||||
}, onMessageContentEdited: {(chatRoom: ChatRoom, message: ChatMessage) in
|
||||
let indexMessage = self.conversationMessagesSection[0].rows.firstIndex(where: {$0.eventModel.eventLogId == message.messageId})
|
||||
|
||||
var attachmentNameList: String = ""
|
||||
var attachmentList: [Attachment] = []
|
||||
var contentText = ""
|
||||
|
||||
if !message.contents.isEmpty {
|
||||
message.contents.forEach { content in
|
||||
if content.isText && content.name == nil {
|
||||
contentText = content.utf8Text ?? ""
|
||||
} else if content.name != nil && !content.name!.isEmpty {
|
||||
if content.filePath == nil || content.filePath!.isEmpty {
|
||||
let path = URL(string: self.getNewFilePath(name: content.name ?? ""))
|
||||
|
||||
if path != nil {
|
||||
let attachment =
|
||||
Attachment(
|
||||
id: UUID().uuidString,
|
||||
name: content.name!,
|
||||
url: path!,
|
||||
type: .fileTransfer,
|
||||
size: content.fileSize,
|
||||
transferProgressIndication: content.filePath != nil && !content.filePath!.isEmpty ? 100 : -1
|
||||
)
|
||||
attachmentNameList += ", \(content.name!)"
|
||||
attachmentList.append(attachment)
|
||||
}
|
||||
} else {
|
||||
if content.type != "video" {
|
||||
let filePathSep = content.filePath!.components(separatedBy: "/Library/Images/")
|
||||
let path = URL(string: self.getNewFilePath(name: filePathSep[1]))
|
||||
|
||||
var typeTmp: AttachmentType = .other
|
||||
|
||||
switch content.type {
|
||||
case "image":
|
||||
typeTmp = (content.name?.lowercased().hasSuffix("gif"))! ? .gif : .image
|
||||
case "audio":
|
||||
typeTmp = content.isVoiceRecording ? .voiceRecording : .audio
|
||||
case "application":
|
||||
typeTmp = content.subtype.lowercased() == "pdf" ? .pdf : .other
|
||||
case "text":
|
||||
typeTmp = .text
|
||||
default:
|
||||
typeTmp = .other
|
||||
}
|
||||
|
||||
if path != nil {
|
||||
let attachment =
|
||||
Attachment(
|
||||
id: UUID().uuidString,
|
||||
name: content.name!,
|
||||
url: path!,
|
||||
type: typeTmp,
|
||||
duration: typeTmp == .voiceRecording ? content.fileDuration : 0,
|
||||
size: content.fileSize,
|
||||
transferProgressIndication: content.filePath != nil && !content.filePath!.isEmpty ? 100 : -1
|
||||
)
|
||||
attachmentNameList += ", \(content.name!)"
|
||||
attachmentList.append(attachment)
|
||||
if typeTmp != .voiceRecording {
|
||||
DispatchQueue.main.async {
|
||||
if !attachment.full.pathExtension.isEmpty {
|
||||
self.attachments.append(attachment)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if content.type == "video" {
|
||||
let filePathSep = content.filePath!.components(separatedBy: "/Library/Images/")
|
||||
let path = URL(string: self.getNewFilePath(name: filePathSep[1]))
|
||||
let pathThumbnail = URL(string: self.generateThumbnail(name: filePathSep[1]))
|
||||
|
||||
if path != nil && pathThumbnail != nil {
|
||||
let attachment =
|
||||
Attachment(
|
||||
id: UUID().uuidString,
|
||||
name: content.name!,
|
||||
thumbnail: pathThumbnail!,
|
||||
full: path!,
|
||||
type: .video,
|
||||
size: content.fileSize,
|
||||
transferProgressIndication: content.filePath != nil && !content.filePath!.isEmpty ? 100 : -1
|
||||
)
|
||||
attachmentNameList += ", \(content.name!)"
|
||||
attachmentList.append(attachment)
|
||||
DispatchQueue.main.async {
|
||||
if !attachment.full.pathExtension.isEmpty {
|
||||
self.attachments.append(attachment)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !attachmentNameList.isEmpty {
|
||||
attachmentNameList = String(attachmentNameList.dropFirst(2))
|
||||
}
|
||||
|
||||
let indexReplyMessage = self.conversationMessagesSection[0].rows.firstIndex(where: {$0.message.replyMessage?.id == message.messageId})
|
||||
|
||||
DispatchQueue.main.async {
|
||||
if indexMessage != nil {
|
||||
self.conversationMessagesSection[0].rows[indexMessage!].message.text = contentText
|
||||
self.conversationMessagesSection[0].rows[indexMessage!].message.isEdited = true
|
||||
self.conversationMessagesSection[0].rows[indexMessage!].message.attachments = attachmentList
|
||||
self.conversationMessagesSection[0].rows[indexMessage!].message.attachmentsNames = attachmentNameList
|
||||
}
|
||||
|
||||
if indexReplyMessage != nil {
|
||||
self.conversationMessagesSection[0].rows[indexReplyMessage!].message.replyMessage?.text = contentText
|
||||
}
|
||||
}
|
||||
}, onMessageRetracted: {(chatRoom: ChatRoom, message: ChatMessage) in
|
||||
// TODO
|
||||
})
|
||||
|
||||
self.chatRoomDelegateHolder = ChatRoomDelegateHolder(chatroom: chatRoom, delegate: chatRoomDelegate)
|
||||
}
|
||||
|
||||
|
|
@ -544,6 +665,8 @@ class ConversationViewModel: ObservableObject {
|
|||
id: UUID().uuidString,
|
||||
status: nil,
|
||||
isOutgoing: false,
|
||||
isEditable: false,
|
||||
isEdited: false,
|
||||
dateReceived: 0,
|
||||
address: "",
|
||||
isFirstMessage: false,
|
||||
|
|
@ -722,6 +845,8 @@ class ConversationViewModel: ObservableObject {
|
|||
isFirstMessage: false,
|
||||
text: contentReplyText,
|
||||
isOutgoing: false,
|
||||
isEditable: false,
|
||||
isEdited: false,
|
||||
dateReceived: 0,
|
||||
attachmentsNames: attachmentNameReplyList,
|
||||
attachments: []
|
||||
|
|
@ -735,6 +860,8 @@ class ConversationViewModel: ObservableObject {
|
|||
id: !chatMessage.messageId.isEmpty ? chatMessage.messageId : UUID().uuidString,
|
||||
status: statusTmp,
|
||||
isOutgoing: chatMessage.isOutgoing,
|
||||
isEditable: chatMessage.isOutgoing ? chatMessage.isEditable : false,
|
||||
isEdited: chatMessage.isEdited,
|
||||
dateReceived: chatMessage.time,
|
||||
address: addressCleaned?.asStringUriOnly() ?? "",
|
||||
isFirstMessage: isFirstMessageTmp,
|
||||
|
|
@ -788,6 +915,8 @@ class ConversationViewModel: ObservableObject {
|
|||
id: UUID().uuidString,
|
||||
status: nil,
|
||||
isOutgoing: false,
|
||||
isEditable: false,
|
||||
isEdited: false,
|
||||
dateReceived: 0,
|
||||
address: "",
|
||||
isFirstMessage: false,
|
||||
|
|
@ -965,6 +1094,8 @@ class ConversationViewModel: ObservableObject {
|
|||
isFirstMessage: false,
|
||||
text: contentReplyText,
|
||||
isOutgoing: false,
|
||||
isEditable: false,
|
||||
isEdited: false,
|
||||
dateReceived: 0,
|
||||
attachmentsNames: attachmentNameReplyList,
|
||||
attachments: []
|
||||
|
|
@ -978,6 +1109,8 @@ class ConversationViewModel: ObservableObject {
|
|||
id: !chatMessage.messageId.isEmpty ? chatMessage.messageId : UUID().uuidString,
|
||||
status: statusTmp,
|
||||
isOutgoing: chatMessage.isOutgoing,
|
||||
isEditable: chatMessage.isOutgoing ? chatMessage.isEditable : false,
|
||||
isEdited: chatMessage.isEdited,
|
||||
dateReceived: chatMessage.time,
|
||||
address: addressCleaned?.asStringUriOnly() ?? "",
|
||||
isFirstMessage: isFirstMessageTmp,
|
||||
|
|
@ -1048,6 +1181,8 @@ class ConversationViewModel: ObservableObject {
|
|||
id: UUID().uuidString,
|
||||
status: nil,
|
||||
isOutgoing: false,
|
||||
isEditable: false,
|
||||
isEdited: false,
|
||||
dateReceived: 0,
|
||||
address: "",
|
||||
isFirstMessage: false,
|
||||
|
|
@ -1239,6 +1374,8 @@ class ConversationViewModel: ObservableObject {
|
|||
isFirstMessage: false,
|
||||
text: contentReplyText,
|
||||
isOutgoing: false,
|
||||
isEditable: false,
|
||||
isEdited: false,
|
||||
dateReceived: 0,
|
||||
attachmentsNames: attachmentNameReplyList,
|
||||
attachments: []
|
||||
|
|
@ -1253,6 +1390,8 @@ class ConversationViewModel: ObservableObject {
|
|||
appData: chatMessage.appdata ?? "",
|
||||
status: statusTmp,
|
||||
isOutgoing: chatMessage.isOutgoing,
|
||||
isEditable: chatMessage.isOutgoing ? chatMessage.isEditable : false,
|
||||
isEdited: chatMessage.isEdited,
|
||||
dateReceived: chatMessage.time,
|
||||
address: addressCleaned != nil ? addressCleaned!.asStringUriOnly() : "",
|
||||
isFirstMessage: isFirstMessageTmp,
|
||||
|
|
@ -1471,6 +1610,8 @@ class ConversationViewModel: ObservableObject {
|
|||
isFirstMessage: false,
|
||||
text: contentReplyText,
|
||||
isOutgoing: false,
|
||||
isEditable: false,
|
||||
isEdited: false,
|
||||
dateReceived: 0,
|
||||
attachmentsNames: attachmentNameReplyList,
|
||||
attachments: []
|
||||
|
|
@ -1485,6 +1626,8 @@ class ConversationViewModel: ObservableObject {
|
|||
appData: chatMessage.appdata ?? "",
|
||||
status: statusTmp,
|
||||
isOutgoing: chatMessage.isOutgoing,
|
||||
isEditable: chatMessage.isOutgoing ? chatMessage.isEditable : false,
|
||||
isEdited: chatMessage.isEdited,
|
||||
dateReceived: chatMessage.time,
|
||||
address: addressCleaned != nil ? addressCleaned!.asStringUriOnly() : "",
|
||||
isFirstMessage: isFirstMessageTmp,
|
||||
|
|
@ -1526,6 +1669,8 @@ class ConversationViewModel: ObservableObject {
|
|||
id: UUID().uuidString,
|
||||
status: nil,
|
||||
isOutgoing: false,
|
||||
isEditable: false,
|
||||
isEdited: false,
|
||||
dateReceived: 0,
|
||||
address: "",
|
||||
isFirstMessage: false,
|
||||
|
|
@ -1553,6 +1698,9 @@ class ConversationViewModel: ObservableObject {
|
|||
}
|
||||
|
||||
func replyToMessage(index: Int, isMessageTextFocused: Binding<Bool>) {
|
||||
if self.messageToEdit != nil {
|
||||
self.messageToEdit = nil
|
||||
}
|
||||
coreContext.doOnCoreQueue { _ in
|
||||
let messageToReplyTmp = self.conversationMessagesSection[0].rows[index]
|
||||
DispatchQueue.main.async {
|
||||
|
|
@ -1564,6 +1712,21 @@ class ConversationViewModel: ObservableObject {
|
|||
}
|
||||
}
|
||||
|
||||
func editMessage(chatMessage: EventLogMessage, isMessageTextFocused: Binding<Bool>) {
|
||||
if self.messageToReply != nil {
|
||||
self.messageToReply = nil
|
||||
}
|
||||
coreContext.doOnCoreQueue { _ in
|
||||
let messageToEditTmp = chatMessage
|
||||
DispatchQueue.main.async {
|
||||
withAnimation(.linear(duration: 0.15)) {
|
||||
self.messageToEdit = messageToEditTmp
|
||||
}
|
||||
isMessageTextFocused.wrappedValue = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func resendMessage(chatMessage: EventLogMessage) {
|
||||
coreContext.doOnCoreQueue { _ in
|
||||
if let message = chatMessage.eventModel.eventLog.chatMessage {
|
||||
|
|
@ -1619,6 +1782,8 @@ class ConversationViewModel: ObservableObject {
|
|||
id: UUID().uuidString,
|
||||
status: nil,
|
||||
isOutgoing: false,
|
||||
isEditable: false,
|
||||
isEdited: false,
|
||||
dateReceived: 0,
|
||||
address: "",
|
||||
isFirstMessage: false,
|
||||
|
|
@ -1796,6 +1961,8 @@ class ConversationViewModel: ObservableObject {
|
|||
isFirstMessage: false,
|
||||
text: contentReplyText,
|
||||
isOutgoing: false,
|
||||
isEditable: false,
|
||||
isEdited: false,
|
||||
dateReceived: 0,
|
||||
attachmentsNames: attachmentNameReplyList,
|
||||
attachments: []
|
||||
|
|
@ -1809,6 +1976,8 @@ class ConversationViewModel: ObservableObject {
|
|||
id: !chatMessage.messageId.isEmpty ? chatMessage.messageId : UUID().uuidString,
|
||||
status: statusTmp,
|
||||
isOutgoing: chatMessage.isOutgoing,
|
||||
isEditable: chatMessage.isOutgoing ? chatMessage.isEditable : false,
|
||||
isEdited: chatMessage.isEdited,
|
||||
dateReceived: chatMessage.time,
|
||||
address: addressCleaned?.asStringUriOnly() ?? "",
|
||||
isFirstMessage: isFirstMessageTmp,
|
||||
|
|
@ -1873,6 +2042,8 @@ class ConversationViewModel: ObservableObject {
|
|||
if chatMessageToReply != nil {
|
||||
message = try self.sharedMainViewModel.displayedConversation!.chatRoom.createReplyMessage(message: chatMessageToReply!)
|
||||
}
|
||||
} else if let chatMessage = self.messageToEdit?.eventModel.eventLog.chatMessage {
|
||||
message = try self.sharedMainViewModel.displayedConversation!.chatRoom.createReplacesMessage(message: chatMessage)
|
||||
} else {
|
||||
message = try self.sharedMainViewModel.displayedConversation!.chatRoom.createEmptyMessage()
|
||||
}
|
||||
|
|
@ -1948,12 +2119,14 @@ class ConversationViewModel: ObservableObject {
|
|||
if message != nil && !message!.contents.isEmpty {
|
||||
Log.info("[ConversationViewModel] Sending message")
|
||||
message!.send()
|
||||
self.sharedMainViewModel.displayedConversation!.chatRoom.stopComposing()
|
||||
}
|
||||
|
||||
Log.info("[ConversationViewModel] Message sent, re-setting defaults")
|
||||
|
||||
DispatchQueue.main.async {
|
||||
self.messageToReply = nil
|
||||
self.messageToEdit = nil
|
||||
withAnimation {
|
||||
self.mediasToSend.removeAll()
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue