feat: edit messages

This commit is contained in:
Richard Ramos 2021-06-29 10:49:32 -04:00 committed by Iuri Matias
parent 77e501c478
commit c7750da267
19 changed files with 240 additions and 20 deletions

View File

@ -42,6 +42,8 @@ type
PinnedBy = UserRole + 31 PinnedBy = UserRole + 31
GapFrom = UserRole + 32 GapFrom = UserRole + 32
GapTo = UserRole + 33 GapTo = UserRole + 33
Replace = UserRole + 34
IsEdited = UserRole + 35
QtObject: QtObject:
type type
@ -50,11 +52,13 @@ QtObject:
status: Status status: Status
id*: string id*: string
messageIndex: Table[string, int] messageIndex: Table[string, int]
isEdited*: Table[string, bool]
messageReactions*: Table[string, string] messageReactions*: Table[string, string]
timedoutMessages: HashSet[string] timedoutMessages: HashSet[string]
proc delete(self: ChatMessageList) = proc delete(self: ChatMessageList) =
self.messages = @[] self.messages = @[]
self.isEdited = initTable[string, bool]()
self.messageIndex = initTable[string, int]() self.messageIndex = initTable[string, int]()
self.timedoutMessages = initHashSet[string]() self.timedoutMessages = initHashSet[string]()
self.QAbstractListModel.delete self.QAbstractListModel.delete
@ -85,9 +89,17 @@ QtObject:
result.messageIndex = initTable[string, int]() result.messageIndex = initTable[string, int]()
result.timedoutMessages = initHashSet[string]() result.timedoutMessages = initHashSet[string]()
result.isEdited = initTable[string, bool]()
result.status = status result.status = status
result.setup result.setup
proc hasMessage*(self: ChatMessageList, messageId: string): bool =
return self.messageIndex.hasKey(messageId)
proc getMessage*(self: ChatMessageList, messageId: string): Message =
return self.messages[self.messageIndex[messageId]]
proc deleteMessage*(self: ChatMessageList, messageId: string) = proc deleteMessage*(self: ChatMessageList, messageId: string) =
if not self.messageIndex.hasKey(messageId): return if not self.messageIndex.hasKey(messageId): return
let messageIndex = self.messageIndex[messageId] let messageIndex = self.messageIndex[messageId]
@ -95,6 +107,9 @@ QtObject:
self.messages.delete(messageIndex) self.messages.delete(messageIndex)
self.messageIndex.del(messageId) self.messageIndex.del(messageId)
self.messageReactions.del(messageId) self.messageReactions.del(messageId)
# update indexes
for i in countup(0, self.messages.len - 1):
self.messageIndex[self.messages[i].id] = i
self.endRemoveRows() self.endRemoveRows()
proc deleteMessagesByChatId*(self: ChatMessageList, chatId: string) = proc deleteMessagesByChatId*(self: ChatMessageList, chatId: string) =
@ -102,6 +117,14 @@ QtObject:
for message in messages: for message in messages:
self.deleteMessage(message.id) self.deleteMessage(message.id)
proc replaceMessage*(self: ChatMessageList, message: Message) =
let msgIdx = self.messageIndex[message.id]
let topLeft = self.createIndex(msgIdx, 0, nil)
let bottomRight = self.createIndex(msgIdx, 0, nil)
self.messages[msgIdx].parsedText = message.parsedText
self.messages[msgIdx].text = message.text
self.dataChanged(topLeft, bottomRight, @[ChatMessageRoles.Message.int, ChatMessageRoles.PlainText.int, ChatMessageRoles.IsEdited.int])
proc resetTimeOut*(self: ChatMessageList, messageId: string) = proc resetTimeOut*(self: ChatMessageList, messageId: string) =
if not self.messageIndex.hasKey(messageId): return if not self.messageIndex.hasKey(messageId): return
let msgIdx = self.messageIndex[messageId] let msgIdx = self.messageIndex[messageId]
@ -145,6 +168,7 @@ QtObject:
return return
let message = self.messages[index.row] let message = self.messages[index.row]
let chatMessageRole = role.ChatMessageRoles let chatMessageRole = role.ChatMessageRoles
let isEdited = if self.isEdited.hasKey(message.id): self.isEdited[message.id] else: false
case chatMessageRole: case chatMessageRole:
of ChatMessageRoles.UserName: result = newQVariant(message.userName) of ChatMessageRoles.UserName: result = newQVariant(message.userName)
of ChatMessageRoles.Message: result = newQVariant(renderBlock(message, self.status.chat.contacts)) of ChatMessageRoles.Message: result = newQVariant(renderBlock(message, self.status.chat.contacts))
@ -188,6 +212,8 @@ QtObject:
of ChatMessageRoles.LocalName: result = newQVariant(message.localName) of ChatMessageRoles.LocalName: result = newQVariant(message.localName)
of ChatMessageRoles.GapFrom: result = newQVariant(message.gapFrom) of ChatMessageRoles.GapFrom: result = newQVariant(message.gapFrom)
of ChatMessageRoles.GapTo: result = newQVariant(message.gapTo) of ChatMessageRoles.GapTo: result = newQVariant(message.gapTo)
of ChatMessageRoles.Replace: result = newQVariant(message.replace)
of ChatMessageRoles.IsEdited: result = newQVariant(isEdited)
method roleNames(self: ChatMessageList): Table[int, string] = method roleNames(self: ChatMessageList): Table[int, string] =
{ {
@ -222,7 +248,9 @@ QtObject:
ChatMessageRoles.LocalName.int:"localName", ChatMessageRoles.LocalName.int:"localName",
ChatMessageRoles.StickerPackId.int:"stickerPackId", ChatMessageRoles.StickerPackId.int:"stickerPackId",
ChatMessageRoles.GapFrom.int:"gapFrom", ChatMessageRoles.GapFrom.int:"gapFrom",
ChatMessageRoles.GapTo.int:"gapTo" ChatMessageRoles.GapTo.int:"gapTo",
ChatMessageRoles.Replace.int:"replaces",
ChatMessageRoles.IsEdited.int:"isEdited"
}.toTable }.toTable
proc getMessageIndex*(self: ChatMessageList, messageId: string): int {.slot.} = proc getMessageIndex*(self: ChatMessageList, messageId: string): int {.slot.} =
@ -246,10 +274,17 @@ QtObject:
of "image": result = $(message.image) of "image": result = $(message.image)
of "contentType": result = $(message.contentType.int) of "contentType": result = $(message.contentType.int)
of "sticker": result = $(message.stickerHash.decodeContentHash()) of "sticker": result = $(message.stickerHash.decodeContentHash())
of "isEdited": result = if self.isEdited.hasKey(message.id): $self.isEdited[message.id] else: $false
else: result = ("") else: result = ("")
proc add*(self: ChatMessageList, message: Message) = proc add*(self: ChatMessageList, message: Message) =
if self.messageIndex.hasKey(message.id): return # duplicated msg if self.messageIndex.hasKey(message.id) and message.editedAt == "0": return # duplicated msg
if message.editedAt != "0":
self.isEdited[message.id] = true
if self.messageIndex.hasKey(message.id):
self.replaceMessage(message)
return
self.beginInsertRows(newQModelIndex(), self.messages.len, self.messages.len) self.beginInsertRows(newQModelIndex(), self.messages.len, self.messages.len)
self.messageIndex[message.id] = self.messages.len self.messageIndex[message.id] = self.messages.len

View File

@ -118,7 +118,9 @@ QtObject:
result = updatedMessage result = updatedMessage
proc sendMessage*(self: MessageView, message: string, replyTo: string, contentType: int = ContentType.Message.int, isStatusUpdate: bool = false, contactsString: string = "") {.slot.} = proc hideMessage(self: MessageView, mId: string) {.signal.}
proc sendOrEditMessage*(self: MessageView, message: string, replyTo: string, contentType: int = ContentType.Message.int, isStatusUpdate: bool = false, contactsString: string = "", isEdit: bool = false, messageId: string = "") {.slot.} =
let aliasPattern = re(r"(@[A-z][a-z]+ [A-z][a-z]* [A-z][a-z]*)", flags = {reStudy, reIgnoreCase}) let aliasPattern = re(r"(@[A-z][a-z]+ [A-z][a-z]* [A-z][a-z]*)", flags = {reStudy, reIgnoreCase})
let ensPattern = re(r"(@\w+(?=(\.stateofus)?\.eth))", flags = {reStudy, reIgnoreCase}) let ensPattern = re(r"(@\w+(?=(\.stateofus)?\.eth))", flags = {reStudy, reIgnoreCase})
let namePattern = re(r"(@\w+)", flags = {reStudy, reIgnoreCase}) let namePattern = re(r"(@\w+)", flags = {reStudy, reIgnoreCase})
@ -149,7 +151,13 @@ QtObject:
if isStatusUpdate: if isStatusUpdate:
channelId = "@" & self.pubKey channelId = "@" & self.pubKey
self.status.chat.sendMessage(channelId, m, replyTo, contentType) if not isEdit:
self.status.chat.sendMessage(channelId, m, replyTo, contentType)
else:
self.status.chat.editMessage(messageId, m)
proc sendMessage*(self: MessageView, message: string, replyTo: string, contentType: int = ContentType.Message.int, isStatusUpdate: bool = false, contactsString: string = "") {.slot.} =
self.sendOrEditMessage(message, replyTo, contentType, isStatusUpdate, contactsString, false, "")
proc verifyMessageSent*(self: MessageView, data: string) {.slot.} = proc verifyMessageSent*(self: MessageView, data: string) {.slot.} =
let messageData = data.parseJson let messageData = data.parseJson
@ -164,6 +172,12 @@ QtObject:
proc sendingMessageFailed*(self: MessageView) {.signal.} proc sendingMessageFailed*(self: MessageView) {.signal.}
proc messageEdited(self: MessageView, editedMessageId: string, editedMessageContent: string) {.signal.}
proc editMessage*(self: MessageView, messageId: string, originalMessageId: string, message: string, contactsString: string = "") {.slot.} =
self.sendOrEditMessage(message, "", ContentType.Message.int, false, contactsString, true, originalMessageId)
self.messageEdited(originalMessageId, message)
proc messagePushed*(self: MessageView, messageIndex: int) {.signal.} proc messagePushed*(self: MessageView, messageIndex: int) {.signal.}
proc newMessagePushed*(self: MessageView) {.signal.} proc newMessagePushed*(self: MessageView) {.signal.}
@ -359,6 +373,7 @@ QtObject:
proc deleteMessage*(self: MessageView, channelId: string, messageId: string) = proc deleteMessage*(self: MessageView, channelId: string, messageId: string) =
self.messageList[channelId].deleteMessage(messageId) self.messageList[channelId].deleteMessage(messageId)
self.hideMessage(messageId)
proc removeMessagesByUserId(self: MessageView, publicKey: string) {.slot.} = proc removeMessagesByUserId(self: MessageView, publicKey: string) {.slot.} =
for k in self.messageList.keys: for k in self.messageList.keys:

View File

@ -261,7 +261,7 @@ proc setActiveChannel*(self: ChatModel, chatId: string) =
proc processMessageUpdateAfterSend(self: ChatModel, response: string, forceActiveChat: bool = false): (seq[Chat], seq[Message]) = proc processMessageUpdateAfterSend(self: ChatModel, response: string, forceActiveChat: bool = false): (seq[Chat], seq[Message]) =
result = self.processChatUpdate(parseJson(response)) result = self.processChatUpdate(parseJson(response))
var (chats, messages) = result var (chats, messages) = result
if chats.len == 0 or messages.len == 0: if chats.len == 0 and messages.len == 0:
self.events.emit("sendingMessageFailed", MessageArgs()) self.events.emit("sendingMessageFailed", MessageArgs())
else: else:
if (forceActiveChat): if (forceActiveChat):
@ -274,6 +274,10 @@ proc sendMessage*(self: ChatModel, chatId: string, msg: string, replyTo: string
var response = status_chat.sendChatMessage(chatId, msg, replyTo, contentType, communityId) var response = status_chat.sendChatMessage(chatId, msg, replyTo, contentType, communityId)
discard self.processMessageUpdateAfterSend(response, forceActiveChat) discard self.processMessageUpdateAfterSend(response, forceActiveChat)
proc editMessage*(self: ChatModel, messageId: string, msg: string) =
var response = status_chat.editMessage(messageId, msg)
discard self.processMessageUpdateAfterSend(response, false)
proc sendImage*(self: ChatModel, chatId: string, image: string) = proc sendImage*(self: ChatModel, chatId: string, image: string) =
var response = status_chat.sendImageMessage(chatId, image) var response = status_chat.sendImageMessage(chatId, image)
discard self.processMessageUpdateAfterSend(response) discard self.processMessageUpdateAfterSend(response)

View File

@ -14,6 +14,7 @@ type ContentType* {.pure.} = enum
Audio = 8 Audio = 8
Community = 9 Community = 9
Gap = 10 Gap = 10
Edit = 11
type TextItem* = object type TextItem* = object
textType*: string textType*: string
@ -58,6 +59,7 @@ type Message* = object
stickerPackId*: int stickerPackId*: int
text*: string text*: string
timestamp*: string timestamp*: string
editedAt*: string
whisperTimestamp*: string whisperTimestamp*: string
isCurrentUser*: bool isCurrentUser*: bool
stickerHash*: string stickerHash*: string

View File

@ -119,6 +119,14 @@ proc parseReactionsResponse*(chatId: string, rpcResult: JsonNode): (string, seq[
reactions.add(jsonMsg.toReaction) reactions.add(jsonMsg.toReaction)
return (rpcResult{"cursor"}.getStr, reactions) return (rpcResult{"cursor"}.getStr, reactions)
proc editMessage*(messageId: string, msg: string): string =
callPrivateRPC("editMessage".prefix, %* [
{
"id": messageId,
"text": msg
}
])
proc rpcReactions*(chatId: string, cursorVal: JsonNode, limit: int, success: var bool): string = proc rpcReactions*(chatId: string, cursorVal: JsonNode, limit: int, success: var bool): string =
success = true success = true
try: try:
@ -238,6 +246,9 @@ proc leaveGroupChat*(chatId: string): string =
proc clearChatHistory*(chatId: string): string = proc clearChatHistory*(chatId: string): string =
callPrivateRPC("deleteMessagesByChatID".prefix, %* [chatId]) callPrivateRPC("deleteMessagesByChatID".prefix, %* [chatId])
proc deleteMessage*(messageId: string): string =
callPrivateRPC("deleteMessage".prefix, %* [messageId])
proc renameGroup*(chatId: string, newName: string): string = proc renameGroup*(chatId: string, newName: string): string =
callPrivateRPC("changeGroupChatName".prefix, %* [nil, chatId, newName]) callPrivateRPC("changeGroupChatName".prefix, %* [nil, chatId, newName])

View File

@ -301,6 +301,7 @@ proc toMessage*(jsonMsg: JsonNode, pk: string): Message =
localChatId: jsonMsg{"localChatId"}.getStr, localChatId: jsonMsg{"localChatId"}.getStr,
messageType: jsonMsg{"messageType"}.getStr, messageType: jsonMsg{"messageType"}.getStr,
replace: jsonMsg{"replace"}.getStr, replace: jsonMsg{"replace"}.getStr,
editedAt: $jsonMsg{"editedAt"}.getInt,
responseTo: jsonMsg{"responseTo"}.getStr, responseTo: jsonMsg{"responseTo"}.getStr,
rtl: jsonMsg{"rtl"}.getBool, rtl: jsonMsg{"rtl"}.getBool,
seen: jsonMsg{"seen"}.getBool, seen: jsonMsg{"seen"}.getBool,

View File

@ -317,6 +317,8 @@ ScrollView {
sticker: model.sticker sticker: model.sticker
contentType: model.contentType contentType: model.contentType
outgoingStatus: model.outgoingStatus outgoingStatus: model.outgoingStatus
replaces: model.replaces
isEdited: model.isEdited
responseTo: model.responseTo responseTo: model.responseTo
authorCurrentMsg: msgDelegate.ListView.section authorCurrentMsg: msgDelegate.ListView.section
// The previous message is actually the nextSection since we reversed the list order // The previous message is actually the nextSection since we reversed the list order

View File

@ -38,6 +38,9 @@ Item {
property int stickerPackId: -1 property int stickerPackId: -1
property int gapFrom: 0 property int gapFrom: 0
property int gapTo: 0 property int gapTo: 0
property bool isEdit: false
property string replaces: ""
property bool isEdited: false
z: { z: {
if (typeof chatLogView === "undefined") { if (typeof chatLogView === "undefined") {
@ -76,7 +79,7 @@ Item {
property bool isAudio: contentType === Constants.audioType property bool isAudio: contentType === Constants.audioType
property bool isStatusMessage: contentType === Constants.systemMessagePrivateGroupType property bool isStatusMessage: contentType === Constants.systemMessagePrivateGroupType
property bool isSticker: contentType === Constants.stickerType property bool isSticker: contentType === Constants.stickerType
property bool isText: contentType === Constants.messageType property bool isText: contentType === Constants.messageType || contentType === Constants.editType
property bool isMessage: isEmoji || isImage || isSticker || isText || isAudio property bool isMessage: isEmoji || isImage || isSticker || isText || isAudio
|| contentType === Constants.communityInviteType || contentType === Constants.transactionType || contentType === Constants.communityInviteType || contentType === Constants.transactionType
@ -87,6 +90,7 @@ Item {
property string repliedMessageAuthor: replyMessageIndex > -1 ? chatsModel.messageView.messageList.getMessageData(replyMessageIndex, "userName") : ""; property string repliedMessageAuthor: replyMessageIndex > -1 ? chatsModel.messageView.messageList.getMessageData(replyMessageIndex, "userName") : "";
property string repliedMessageAuthorPubkey: replyMessageIndex > -1 ? chatsModel.messageView.messageList.getMessageData(replyMessageIndex, "publicKey") : ""; property string repliedMessageAuthorPubkey: replyMessageIndex > -1 ? chatsModel.messageView.messageList.getMessageData(replyMessageIndex, "publicKey") : "";
property bool repliedMessageAuthorIsCurrentUser: replyMessageIndex > -1 ? repliedMessageAuthorPubkey === profileModel.profile.pubKey : ""; property bool repliedMessageAuthorIsCurrentUser: replyMessageIndex > -1 ? repliedMessageAuthorPubkey === profileModel.profile.pubKey : "";
property bool repliedMessageIsEdited: replyMessageIndex > -1 ? chatsModel.messageView.messageList.getMessageData(replyMessageIndex, "isEdited") === "true" : false;
property string repliedMessageContent: replyMessageIndex > -1 ? chatsModel.messageView.messageList.getMessageData(replyMessageIndex, "message") : ""; property string repliedMessageContent: replyMessageIndex > -1 ? chatsModel.messageView.messageList.getMessageData(replyMessageIndex, "message") : "";
property int repliedMessageType: replyMessageIndex > -1 ? parseInt(chatsModel.messageView.messageList.getMessageData(replyMessageIndex, "contentType")) : 0; property int repliedMessageType: replyMessageIndex > -1 ? parseInt(chatsModel.messageView.messageList.getMessageData(replyMessageIndex, "contentType")) : 0;
property string repliedMessageImage: replyMessageIndex > -1 ? chatsModel.messageView.messageList.getMessageData(replyMessageIndex, "image") : ""; property string repliedMessageImage: replyMessageIndex > -1 ? chatsModel.messageView.messageList.getMessageData(replyMessageIndex, "image") : "";
@ -166,6 +170,17 @@ Item {
} }
} }
Connections {
target: chatsModel.messageView
onHideMessage: {
// This hack is used because message_list deleteMessage sometimes does not remove the messages (there might be an issue with the delegate model)
if(mId === messageId){
root.visible = 0;
root.height = 0;
}
}
}
id: root id: root
width: parent.width width: parent.width
anchors.right: !isCurrentUser ? undefined : parent.right anchors.right: !isCurrentUser ? undefined : parent.right

View File

@ -101,6 +101,28 @@ Rectangle {
} }
} }
Loader {
id: editBtn
active: isText && !isEdit && isCurrentUser
sourceComponent: StatusIconButton {
id: btn
icon.name: "edit-message"
width: 32
height: 32
onClicked: {
isEdit = true
}
onHoveredChanged: {
buttonsContainer.hoverChanged(btn.hovered)
}
StatusToolTip {
visible: btn.hovered
text: qsTr("Edit")
}
}
}
StatusIconButton { StatusIconButton {
id: otherBtn id: otherBtn
icon.name: "dots-icon" icon.name: "dots-icon"

View File

@ -91,6 +91,15 @@ Loader {
profileImage: repliedMessageUserImage profileImage: repliedMessageUserImage
} }
Connections {
target: chatsModel.messageView
onMessageEdited: {
if (responseTo === editedMessageId){
lblReplyMessage.text = Utils.getReplyMessageStyle(Emoji.parse(Utils.linkifyAndXSS(editedMessageContent + Constants.editLabel), Emoji.size.small), isCurrentUser, appSettings.useCompactMode)
}
}
}
StyledTextEdit { StyledTextEdit {
id: lblReplyAuthor id: lblReplyAuthor
text: repliedMessageAuthor text: repliedMessageAuthor
@ -141,7 +150,14 @@ Loader {
Component.onCompleted: textFieldImplicitWidth = implicitWidth Component.onCompleted: textFieldImplicitWidth = implicitWidth
anchors.top: lblReplyAuthor.bottom anchors.top: lblReplyAuthor.bottom
anchors.topMargin: nameMargin anchors.topMargin: nameMargin
text: Utils.getReplyMessageStyle(Emoji.parse(Utils.linkifyAndXSS(repliedMessageContent), Emoji.size.small), isCurrentUser, appSettings.useCompactMode) text: {
if (repliedMessageIsEdited){
let index = repliedMessageContent.length - 4
return Utils.getReplyMessageStyle(Emoji.parse(Utils.linkifyAndXSS(repliedMessageContent.slice(0, index) + Constants.editLabel + repliedMessageContent.slice(index)), Emoji.size.small), isCurrentUser, appSettings.useCompactMode)
} else {
return Utils.getReplyMessageStyle(Emoji.parse(Utils.linkifyAndXSS(repliedMessageContent), Emoji.size.small), isCurrentUser, appSettings.useCompactMode)
}
}
textFormat: Text.RichText textFormat: Text.RichText
color: root.elementsColor color: root.elementsColor
readOnly: true readOnly: true

View File

@ -11,7 +11,7 @@ Item {
property alias textField: chatText property alias textField: chatText
id: root id: root
visible: contentType === Constants.messageType || isEmoji visible: isText || isEmoji
z: 51 z: 51
implicitHeight: visible ? (showMoreLoader.active ? childrenRect.height - 10 : chatText.height) : 0 implicitHeight: visible ? (showMoreLoader.active ? childrenRect.height - 10 : chatText.height) : 0
@ -87,6 +87,10 @@ Item {
if(isEmoji) { if(isEmoji) {
return Emoji.parse(msg, Emoji.size.middle); return Emoji.parse(msg, Emoji.size.middle);
} else { } else {
if(isEdited){
let index = msg.length - 4
return Utils.getMessageWithStyle(Emoji.parse(msg.slice(0, index) + Constants.editLabel + msg.slice(index)), appSettings.useCompactMode, isCurrentUser, hoveredLink)
}
return Utils.getMessageWithStyle(Emoji.parse(msg), appSettings.useCompactMode, isCurrentUser, hoveredLink) return Utils.getMessageWithStyle(Emoji.parse(msg), appSettings.useCompactMode, isCurrentUser, hoveredLink)
} }
} }

View File

@ -33,7 +33,7 @@ Item {
ChatButtons { ChatButtons {
contentType: root.contentType contentType: root.contentType
parentIsHovered: root.isHovered parentIsHovered: !isEdit && root.isHovered
onHoverChanged: hovered && setHovered(messageId, hovered) onHoverChanged: hovered && setHovered(messageId, hovered)
anchors.right: parent.right anchors.right: parent.right
anchors.rightMargin: 20 anchors.rightMargin: 20
@ -73,9 +73,14 @@ Item {
+ (emojiReactionLoader.active ? emojiReactionLoader.height: 0) + (emojiReactionLoader.active ? emojiReactionLoader.height: 0)
+ (retry.visible && !chatTime.visible ? Style.current.smallPadding : 0) + (retry.visible && !chatTime.visible ? Style.current.smallPadding : 0)
+ (pinnedRectangleLoader.active ? Style.current.smallPadding : 0) + (pinnedRectangleLoader.active ? Style.current.smallPadding : 0)
+ (isEdit ? 25 : 0)
width: parent.width width: parent.width
color: { color: {
if (isEdit) {
return Style.current.backgroundHoverLight
}
if (activityCenterMessage) { if (activityCenterMessage) {
return read ? Style.current.transparent : Utils.setColorAlpha(Style.current.blue, 0.1) return read ? Style.current.transparent : Utils.setColorAlpha(Style.current.blue, 0.1)
} }
@ -157,7 +162,7 @@ Item {
UsernameLabel { UsernameLabel {
id: chatName id: chatName
visible: isMessage && headerRepeatCondition visible: !isEdit && isMessage && headerRepeatCondition
anchors.leftMargin: root.chatHorizontalPadding anchors.leftMargin: root.chatHorizontalPadding
anchors.top: chatImage.top anchors.top: chatImage.top
anchors.left: chatImage.right anchors.left: chatImage.right
@ -165,13 +170,68 @@ Item {
ChatTime { ChatTime {
id: chatTime id: chatTime
visible: headerRepeatCondition visible: !isEdit && headerRepeatCondition
anchors.verticalCenter: chatName.verticalCenter anchors.verticalCenter: chatName.verticalCenter
anchors.left: chatName.right anchors.left: chatName.right
anchors.leftMargin: 4 anchors.leftMargin: 4
color: Style.current.secondaryText color: Style.current.secondaryText
} }
Loader {
id: editMessageLoader
active: isEdit
anchors.top: chatReply.active ? chatReply.bottom : parent.top
anchors.left: chatImage.right
anchors.leftMargin: root.chatHorizontalPadding
anchors.right: parent.right
anchors.rightMargin: root.chatHorizontalPadding
height: (item !== null && typeof(item)!== 'undefined')? item.height: 0
sourceComponent: Item {
id: editText
height: childrenRect.height
StatusChatInput {
Component.onCompleted: {
textInput.forceActiveFocus();
textInput.cursorPosition = textInput.length
}
id: editTextInput
chatInputPlaceholder: qsTrId("type-a-message-")
chatType: chatsModel.channelView.activeChannel.chatType
isEdit: true
textInput.text: Emoji.parse(message.replace(/(<a href="\/\/0x[0-9A-Fa-f]+" class="mention">)/g, "$1@"))
}
StatusButton {
id: cancelBtn
anchors.left: parent.left
anchors.leftMargin: Style.current.halfPadding
anchors.top: editTextInput.bottom
bgColor: Style.current.transparent
text: qsTr("Cancel")
onClicked: {
isEdit = false
editTextInput.textInput.text = Emoji.parse(message)
}
}
StatusButton {
id: saveBtn
anchors.left: cancelBtn.right
anchors.leftMargin: Style.current.halfPadding
anchors.top: editTextInput.bottom
text: qsTr("Save")
onClicked: {
let msg = chatsModel.plainText(Emoji.deparse(editTextInput.textInput.text))
if (msg.length > 0){
msg = chatInput.interpretMessage(msg)
isEdit = false
chatsModel.messageView.editMessage(messageId, contentType == Constants.editType ? replaces : messageId, msg, JSON.stringify(suggestionsObj));
}
}
}
}
}
Item { Item {
id: messageContent id: messageContent
height: childrenRect.height + (isEmoji ? 2 : 0) height: childrenRect.height + (isEmoji ? 2 : 0)
@ -182,7 +242,8 @@ Item {
anchors.leftMargin: root.chatHorizontalPadding anchors.leftMargin: root.chatHorizontalPadding
anchors.right: parent.right anchors.right: parent.right
anchors.rightMargin: root.chatHorizontalPadding anchors.rightMargin: root.chatHorizontalPadding
visible: !isEdit
ChatText { ChatText {
readonly property int leftPadding: chatImage.anchors.leftMargin + chatImage.width + root.chatHorizontalPadding readonly property int leftPadding: chatImage.anchors.leftMargin + chatImage.width + root.chatHorizontalPadding
id: chatText id: chatText

View File

@ -14,6 +14,8 @@ PopupMenu {
property bool emojiOnly: false property bool emojiOnly: false
property bool hideEmojiPicker: false property bool hideEmojiPicker: false
property bool pinnedMessage: false property bool pinnedMessage: false
property bool isText: false
property bool isCurrentUser: false
property string linkUrls: "" property string linkUrls: ""
property alias emojiContainer: emojiContainer property alias emojiContainer: emojiContainer
@ -26,6 +28,8 @@ PopupMenu {
property var fromAuthor: "" property var fromAuthor: ""
property var text: "" property var text: ""
property var emojiReactionsReactedByUser: [] property var emojiReactionsReactedByUser: []
property var onClickEdit: function(){}
subMenuIcons: [ subMenuIcons: [
{ {
source: Qt.resolvedUrl("../../../../shared/img/copy-to-clipboard-icon"), source: Qt.resolvedUrl("../../../../shared/img/copy-to-clipboard-icon"),
@ -236,6 +240,19 @@ PopupMenu {
icon.height: 16 icon.height: 16
enabled: !emojiOnly && !copyLinkAction.enabled enabled: !emojiOnly && !copyLinkAction.enabled
} }
Action {
id: editMessageAction
text: qsTr("Edit message")
onTriggered: {
onClickEdit();
}
icon.source: "../../../img/profileActive.svg"
icon.width: 16
icon.height: 16
enabled: isCurrentUser && isText
}
Action { Action {
text: messageContextMenu.isProfile ? text: messageContextMenu.isProfile ?
//% "Send message" //% "Send message"

View File

@ -0,0 +1,3 @@
<svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.8383 2.97542C15.9943 1.81939 17.8686 1.81939 19.0246 2.97542C20.1807 4.13145 20.1807 6.00574 19.0246 7.16177L8.25225 17.9342C7.86066 18.3257 7.37575 18.6111 6.84327 18.7632L2.98627 19.8652C2.74577 19.9339 2.48693 19.8669 2.31007 19.69C2.13321 19.5131 2.06614 19.2543 2.13485 19.0138L3.23686 15.1568C3.38899 14.6243 3.67432 14.1394 4.0659 13.7478L14.8383 2.97542ZM18.0507 3.9494C17.4325 3.33129 16.4304 3.33129 15.8123 3.9494L5.03988 14.7218C4.81382 14.9479 4.6491 15.2278 4.56128 15.5352C4.2294 16.6968 5.3033 17.7707 6.46487 17.4388C6.77226 17.351 7.0522 17.1862 7.27826 16.9602L18.0507 6.18779C18.6688 5.56967 18.6688 4.56751 18.0507 3.9494Z" fill="#4360DF"/>
</svg>

After

Width:  |  Height:  |  Size: 818 B

View File

@ -68,6 +68,7 @@ QtObject {
readonly property int audioType: 8 readonly property int audioType: 8
readonly property int communityInviteType: 9 readonly property int communityInviteType: 9
readonly property int gapType: 10 readonly property int gapType: 10
readonly property int editType: 11
readonly property string watchWalletType: "watch" readonly property string watchWalletType: "watch"
readonly property string keyWalletType: "key" readonly property string keyWalletType: "key"
@ -147,6 +148,8 @@ QtObject {
readonly property string ens_connected: "connected" readonly property string ens_connected: "connected"
readonly property string ens_connected_dkey: "connected-different-key" readonly property string ens_connected_dkey: "connected-different-key"
readonly property string editLabel: ` <span class="isEdited">` + qsTr("(edited)") + `</span>`
readonly property var ensState: { readonly property var ensState: {
//% "Username already taken :(" //% "Username already taken :("
"taken": qsTrId("ens-username-taken"), "taken": qsTrId("ens-username-taken"),

View File

@ -29,6 +29,7 @@ Theme {
property color green: "#4EBC60" property color green: "#4EBC60"
property color turquoise: "#007b7d" property color turquoise: "#007b7d"
property color tenPercentWhite: Qt.rgba(255, 255, 255, 0.1) property color tenPercentWhite: Qt.rgba(255, 255, 255, 0.1)
property color fivePercentBlack: "#E5E5E5"
property color tenPercentBlue: Qt.rgba(67, 96, 223, 0.1) property color tenPercentBlue: Qt.rgba(67, 96, 223, 0.1)
property color background: "#2C2C2C" property color background: "#2C2C2C"
@ -43,6 +44,7 @@ Theme {
property color currentUserTextColor: white property color currentUserTextColor: white
property color secondaryBackground: "#353a4d" property color secondaryBackground: "#353a4d"
property color inputBackground: darkGrey property color inputBackground: darkGrey
property color secondaryinputBackground: fivePercentBlack
property color inputBorderFocus: blue property color inputBorderFocus: blue
property color secondaryMenuBorder: darkGrey property color secondaryMenuBorder: darkGrey
property color inputColor: textColor property color inputColor: textColor

View File

@ -29,6 +29,7 @@ Theme {
property color green: "#4EBC60" property color green: "#4EBC60"
property color turquoise: "#007b7d" property color turquoise: "#007b7d"
property color tenPercentBlack: Qt.rgba(0, 0, 0, 0.1) property color tenPercentBlack: Qt.rgba(0, 0, 0, 0.1)
property color fivePercentBlack: "#E5E5E5"
property color tenPercentBlue: Qt.rgba(67, 96, 223, 0.1) property color tenPercentBlue: Qt.rgba(67, 96, 223, 0.1)
property color background: white property color background: white
@ -43,6 +44,7 @@ Theme {
property color currentUserTextColor: white property color currentUserTextColor: white
property color secondaryBackground: lightBlue property color secondaryBackground: lightBlue
property color inputBackground: grey property color inputBackground: grey
property color secondaryinputBackground: fivePercentBlack
property color inputBorderFocus: blue property color inputBorderFocus: blue
property color secondaryMenuBorder: grey3 property color secondaryMenuBorder: grey3
property color inputColor: black property color inputColor: black

View File

@ -85,6 +85,10 @@ QtObject {
`.emoji {` + `.emoji {` +
`vertical-align: bottom;` + `vertical-align: bottom;` +
`}` + `}` +
`span.isEdited {` +
`color: ${Style.current.secondaryText};` +
`margin-left: 5px` +
`}` +
`</style>` + `</style>` +
`${msg}` `${msg}`
} }

View File

@ -23,6 +23,7 @@ Rectangle {
property bool isColonPressed: false; property bool isColonPressed: false;
property bool isReply: false property bool isReply: false
property bool isImage: false property bool isImage: false
property bool isEdit: false
property var recentStickers property var recentStickers
property var stickerPackList property var stickerPackList
@ -66,8 +67,8 @@ Rectangle {
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
color: Style.current.background color: Style.current.transparent
function calculateExtraHeightFactor() { function calculateExtraHeightFactor() {
const factor = (messageInputField.length / 500) + 1; const factor = (messageInputField.length / 500) + 1;
return (factor > 5) ? 5 : factor; return (factor > 5) ? 5 : factor;
@ -694,7 +695,7 @@ Rectangle {
anchors.leftMargin: 4 anchors.leftMargin: 4
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.bottomMargin: 16 anchors.bottomMargin: 16
visible: control.chatType === Constants.chatTypeOneToOne && !control.isStatusUpdateInput visible: !isEdit && control.chatType === Constants.chatTypeOneToOne && !control.isStatusUpdateInput
onClicked: { onClicked: {
highlighted = true highlighted = true
chatCommandsPopup.open() chatCommandsPopup.open()
@ -710,8 +711,8 @@ Rectangle {
anchors.leftMargin: chatCommandsBtn.visible ? 2 : 4 anchors.leftMargin: chatCommandsBtn.visible ? 2 : 4
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.bottomMargin: 16 anchors.bottomMargin: 16
visible: control.chatType !== Constants.chatTypePublic && !control.isStatusUpdateInput visible: !isEdit && control.chatType !== Constants.chatTypePublic && !control.isStatusUpdateInput
onClicked: { onClicked: {
highlighted = true highlighted = true
imageDialog.open() imageDialog.open()
@ -739,7 +740,7 @@ Rectangle {
return messageInputField.implicitHeight return messageInputField.implicitHeight
} }
color: Style.current.inputBackground color: isEdit ? Style.current.secondaryinputBackground : Style.current.inputBackground
radius: control.isStatusUpdateInput ? 36 : radius: control.isStatusUpdateInput ? 36 :
height > defaultInputFieldHeight + 1 || extendedArea.visible ? 16 : 32 height > defaultInputFieldHeight + 1 || extendedArea.visible ? 16 : 32
@ -783,7 +784,7 @@ Rectangle {
anchors.bottom: control.isStatusUpdateInput ? undefined : messageInput.top anchors.bottom: control.isStatusUpdateInput ? undefined : messageInput.top
anchors.top: control.isStatusUpdateInput ? messageInput.bottom : undefined anchors.top: control.isStatusUpdateInput ? messageInput.bottom : undefined
anchors.topMargin: control.isStatusUpdateInput ? -Style.current.halfPadding : 0 anchors.topMargin: control.isStatusUpdateInput ? -Style.current.halfPadding : 0
color: Style.current.inputBackground color: isEdit ? Style.current.secondaryinputBackground : Style.current.inputBackground
radius: control.isStatusUpdateInput ? 36 : 16 radius: control.isStatusUpdateInput ? 36 : 16
Rectangle { Rectangle {
@ -1091,7 +1092,7 @@ Rectangle {
anchors.leftMargin: 2 anchors.leftMargin: 2
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
icon.name: "stickers_icon" icon.name: "stickers_icon"
visible: profileModel.network.current === Constants.networkMainnet && emojiBtn.visible visible: !isEdit && profileModel.network.current === Constants.networkMainnet && emojiBtn.visible
width: visible ? 32 : 0 width: visible ? 32 : 0
type: "secondary" type: "secondary"
onClicked: { onClicked: {