diff --git a/src/app/chat/views/message_list.nim b/src/app/chat/views/message_list.nim index 08e848ce49..3f0396eabd 100644 --- a/src/app/chat/views/message_list.nim +++ b/src/app/chat/views/message_list.nim @@ -42,6 +42,8 @@ type PinnedBy = UserRole + 31 GapFrom = UserRole + 32 GapTo = UserRole + 33 + Replace = UserRole + 34 + IsEdited = UserRole + 35 QtObject: type @@ -50,11 +52,13 @@ QtObject: status: Status id*: string messageIndex: Table[string, int] + isEdited*: Table[string, bool] messageReactions*: Table[string, string] timedoutMessages: HashSet[string] proc delete(self: ChatMessageList) = self.messages = @[] + self.isEdited = initTable[string, bool]() self.messageIndex = initTable[string, int]() self.timedoutMessages = initHashSet[string]() self.QAbstractListModel.delete @@ -85,9 +89,17 @@ QtObject: result.messageIndex = initTable[string, int]() result.timedoutMessages = initHashSet[string]() + result.isEdited = initTable[string, bool]() result.status = status 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) = if not self.messageIndex.hasKey(messageId): return let messageIndex = self.messageIndex[messageId] @@ -95,6 +107,9 @@ QtObject: self.messages.delete(messageIndex) self.messageIndex.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() proc deleteMessagesByChatId*(self: ChatMessageList, chatId: string) = @@ -102,6 +117,14 @@ QtObject: for message in messages: 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) = if not self.messageIndex.hasKey(messageId): return let msgIdx = self.messageIndex[messageId] @@ -145,6 +168,7 @@ QtObject: return let message = self.messages[index.row] let chatMessageRole = role.ChatMessageRoles + let isEdited = if self.isEdited.hasKey(message.id): self.isEdited[message.id] else: false case chatMessageRole: of ChatMessageRoles.UserName: result = newQVariant(message.userName) 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.GapFrom: result = newQVariant(message.gapFrom) 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] = { @@ -222,7 +248,9 @@ QtObject: ChatMessageRoles.LocalName.int:"localName", ChatMessageRoles.StickerPackId.int:"stickerPackId", ChatMessageRoles.GapFrom.int:"gapFrom", - ChatMessageRoles.GapTo.int:"gapTo" + ChatMessageRoles.GapTo.int:"gapTo", + ChatMessageRoles.Replace.int:"replaces", + ChatMessageRoles.IsEdited.int:"isEdited" }.toTable proc getMessageIndex*(self: ChatMessageList, messageId: string): int {.slot.} = @@ -246,10 +274,17 @@ QtObject: of "image": result = $(message.image) of "contentType": result = $(message.contentType.int) of "sticker": result = $(message.stickerHash.decodeContentHash()) + of "isEdited": result = if self.isEdited.hasKey(message.id): $self.isEdited[message.id] else: $false else: result = ("") 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.messageIndex[message.id] = self.messages.len diff --git a/src/app/chat/views/messages.nim b/src/app/chat/views/messages.nim index a3857ea398..e65fb993a0 100644 --- a/src/app/chat/views/messages.nim +++ b/src/app/chat/views/messages.nim @@ -118,7 +118,9 @@ QtObject: 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 ensPattern = re(r"(@\w+(?=(\.stateofus)?\.eth))", flags = {reStudy, reIgnoreCase}) let namePattern = re(r"(@\w+)", flags = {reStudy, reIgnoreCase}) @@ -149,7 +151,13 @@ QtObject: if isStatusUpdate: 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.} = let messageData = data.parseJson @@ -164,6 +172,12 @@ QtObject: 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 newMessagePushed*(self: MessageView) {.signal.} @@ -359,6 +373,7 @@ QtObject: proc deleteMessage*(self: MessageView, channelId: string, messageId: string) = self.messageList[channelId].deleteMessage(messageId) + self.hideMessage(messageId) proc removeMessagesByUserId(self: MessageView, publicKey: string) {.slot.} = for k in self.messageList.keys: diff --git a/src/status/chat.nim b/src/status/chat.nim index d953633b94..43ac209409 100644 --- a/src/status/chat.nim +++ b/src/status/chat.nim @@ -261,7 +261,7 @@ proc setActiveChannel*(self: ChatModel, chatId: string) = proc processMessageUpdateAfterSend(self: ChatModel, response: string, forceActiveChat: bool = false): (seq[Chat], seq[Message]) = result = self.processChatUpdate(parseJson(response)) 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()) else: 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) 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) = var response = status_chat.sendImageMessage(chatId, image) discard self.processMessageUpdateAfterSend(response) diff --git a/src/status/chat/message.nim b/src/status/chat/message.nim index 68775439af..94061f8af9 100644 --- a/src/status/chat/message.nim +++ b/src/status/chat/message.nim @@ -14,6 +14,7 @@ type ContentType* {.pure.} = enum Audio = 8 Community = 9 Gap = 10 + Edit = 11 type TextItem* = object textType*: string @@ -58,6 +59,7 @@ type Message* = object stickerPackId*: int text*: string timestamp*: string + editedAt*: string whisperTimestamp*: string isCurrentUser*: bool stickerHash*: string diff --git a/src/status/libstatus/chat.nim b/src/status/libstatus/chat.nim index 236f2d28e6..062e5f4ed8 100644 --- a/src/status/libstatus/chat.nim +++ b/src/status/libstatus/chat.nim @@ -119,6 +119,14 @@ proc parseReactionsResponse*(chatId: string, rpcResult: JsonNode): (string, seq[ reactions.add(jsonMsg.toReaction) 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 = success = true try: @@ -238,6 +246,9 @@ proc leaveGroupChat*(chatId: string): string = proc clearChatHistory*(chatId: string): string = callPrivateRPC("deleteMessagesByChatID".prefix, %* [chatId]) +proc deleteMessage*(messageId: string): string = + callPrivateRPC("deleteMessage".prefix, %* [messageId]) + proc renameGroup*(chatId: string, newName: string): string = callPrivateRPC("changeGroupChatName".prefix, %* [nil, chatId, newName]) diff --git a/src/status/signals/messages.nim b/src/status/signals/messages.nim index 4829db7a52..7fff826d7e 100644 --- a/src/status/signals/messages.nim +++ b/src/status/signals/messages.nim @@ -301,6 +301,7 @@ proc toMessage*(jsonMsg: JsonNode, pk: string): Message = localChatId: jsonMsg{"localChatId"}.getStr, messageType: jsonMsg{"messageType"}.getStr, replace: jsonMsg{"replace"}.getStr, + editedAt: $jsonMsg{"editedAt"}.getInt, responseTo: jsonMsg{"responseTo"}.getStr, rtl: jsonMsg{"rtl"}.getBool, seen: jsonMsg{"seen"}.getBool, diff --git a/ui/app/AppLayouts/Chat/ChatColumn/ChatMessages.qml b/ui/app/AppLayouts/Chat/ChatColumn/ChatMessages.qml index 40aa841c75..7c796c752d 100644 --- a/ui/app/AppLayouts/Chat/ChatColumn/ChatMessages.qml +++ b/ui/app/AppLayouts/Chat/ChatColumn/ChatMessages.qml @@ -317,6 +317,8 @@ ScrollView { sticker: model.sticker contentType: model.contentType outgoingStatus: model.outgoingStatus + replaces: model.replaces + isEdited: model.isEdited responseTo: model.responseTo authorCurrentMsg: msgDelegate.ListView.section // The previous message is actually the nextSection since we reversed the list order diff --git a/ui/app/AppLayouts/Chat/ChatColumn/Message.qml b/ui/app/AppLayouts/Chat/ChatColumn/Message.qml index 76dbcc4e06..19ce3f1e28 100644 --- a/ui/app/AppLayouts/Chat/ChatColumn/Message.qml +++ b/ui/app/AppLayouts/Chat/ChatColumn/Message.qml @@ -38,6 +38,9 @@ Item { property int stickerPackId: -1 property int gapFrom: 0 property int gapTo: 0 + property bool isEdit: false + property string replaces: "" + property bool isEdited: false z: { if (typeof chatLogView === "undefined") { @@ -76,7 +79,7 @@ Item { property bool isAudio: contentType === Constants.audioType property bool isStatusMessage: contentType === Constants.systemMessagePrivateGroupType 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 || contentType === Constants.communityInviteType || contentType === Constants.transactionType @@ -87,6 +90,7 @@ Item { property string repliedMessageAuthor: replyMessageIndex > -1 ? chatsModel.messageView.messageList.getMessageData(replyMessageIndex, "userName") : ""; property string repliedMessageAuthorPubkey: replyMessageIndex > -1 ? chatsModel.messageView.messageList.getMessageData(replyMessageIndex, "publicKey") : ""; 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 int repliedMessageType: replyMessageIndex > -1 ? parseInt(chatsModel.messageView.messageList.getMessageData(replyMessageIndex, "contentType")) : 0; 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 width: parent.width anchors.right: !isCurrentUser ? undefined : parent.right diff --git a/ui/app/AppLayouts/Chat/ChatColumn/MessageComponents/ChatButtons.qml b/ui/app/AppLayouts/Chat/ChatColumn/MessageComponents/ChatButtons.qml index cb9625769d..5abf5da94f 100644 --- a/ui/app/AppLayouts/Chat/ChatColumn/MessageComponents/ChatButtons.qml +++ b/ui/app/AppLayouts/Chat/ChatColumn/MessageComponents/ChatButtons.qml @@ -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 { id: otherBtn icon.name: "dots-icon" diff --git a/ui/app/AppLayouts/Chat/ChatColumn/MessageComponents/ChatReply.qml b/ui/app/AppLayouts/Chat/ChatColumn/MessageComponents/ChatReply.qml index 529ecd1f84..588e99556f 100644 --- a/ui/app/AppLayouts/Chat/ChatColumn/MessageComponents/ChatReply.qml +++ b/ui/app/AppLayouts/Chat/ChatColumn/MessageComponents/ChatReply.qml @@ -91,6 +91,15 @@ Loader { 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 { id: lblReplyAuthor text: repliedMessageAuthor @@ -141,7 +150,14 @@ Loader { Component.onCompleted: textFieldImplicitWidth = implicitWidth anchors.top: lblReplyAuthor.bottom 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 color: root.elementsColor readOnly: true diff --git a/ui/app/AppLayouts/Chat/ChatColumn/MessageComponents/ChatText.qml b/ui/app/AppLayouts/Chat/ChatColumn/MessageComponents/ChatText.qml index 4b14baf844..2e7e1e2c68 100644 --- a/ui/app/AppLayouts/Chat/ChatColumn/MessageComponents/ChatText.qml +++ b/ui/app/AppLayouts/Chat/ChatColumn/MessageComponents/ChatText.qml @@ -11,7 +11,7 @@ Item { property alias textField: chatText id: root - visible: contentType === Constants.messageType || isEmoji + visible: isText || isEmoji z: 51 implicitHeight: visible ? (showMoreLoader.active ? childrenRect.height - 10 : chatText.height) : 0 @@ -87,6 +87,10 @@ Item { if(isEmoji) { return Emoji.parse(msg, Emoji.size.middle); } 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) } } diff --git a/ui/app/AppLayouts/Chat/ChatColumn/MessageComponents/CompactMessage.qml b/ui/app/AppLayouts/Chat/ChatColumn/MessageComponents/CompactMessage.qml index 16f8851a6a..db7d1134e3 100644 --- a/ui/app/AppLayouts/Chat/ChatColumn/MessageComponents/CompactMessage.qml +++ b/ui/app/AppLayouts/Chat/ChatColumn/MessageComponents/CompactMessage.qml @@ -33,7 +33,7 @@ Item { ChatButtons { contentType: root.contentType - parentIsHovered: root.isHovered + parentIsHovered: !isEdit && root.isHovered onHoverChanged: hovered && setHovered(messageId, hovered) anchors.right: parent.right anchors.rightMargin: 20 @@ -73,9 +73,14 @@ Item { + (emojiReactionLoader.active ? emojiReactionLoader.height: 0) + (retry.visible && !chatTime.visible ? Style.current.smallPadding : 0) + (pinnedRectangleLoader.active ? Style.current.smallPadding : 0) + + (isEdit ? 25 : 0) width: parent.width color: { + if (isEdit) { + return Style.current.backgroundHoverLight + } + if (activityCenterMessage) { return read ? Style.current.transparent : Utils.setColorAlpha(Style.current.blue, 0.1) } @@ -157,7 +162,7 @@ Item { UsernameLabel { id: chatName - visible: isMessage && headerRepeatCondition + visible: !isEdit && isMessage && headerRepeatCondition anchors.leftMargin: root.chatHorizontalPadding anchors.top: chatImage.top anchors.left: chatImage.right @@ -165,13 +170,68 @@ Item { ChatTime { id: chatTime - visible: headerRepeatCondition + visible: !isEdit && headerRepeatCondition anchors.verticalCenter: chatName.verticalCenter anchors.left: chatName.right anchors.leftMargin: 4 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(/()/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 { id: messageContent height: childrenRect.height + (isEmoji ? 2 : 0) @@ -182,7 +242,8 @@ Item { anchors.leftMargin: root.chatHorizontalPadding anchors.right: parent.right anchors.rightMargin: root.chatHorizontalPadding - + visible: !isEdit + ChatText { readonly property int leftPadding: chatImage.anchors.leftMargin + chatImage.width + root.chatHorizontalPadding id: chatText diff --git a/ui/app/AppLayouts/Chat/components/MessageContextMenu.qml b/ui/app/AppLayouts/Chat/components/MessageContextMenu.qml index ac56ca2080..b33e0ff04c 100644 --- a/ui/app/AppLayouts/Chat/components/MessageContextMenu.qml +++ b/ui/app/AppLayouts/Chat/components/MessageContextMenu.qml @@ -14,6 +14,8 @@ PopupMenu { property bool emojiOnly: false property bool hideEmojiPicker: false property bool pinnedMessage: false + property bool isText: false + property bool isCurrentUser: false property string linkUrls: "" property alias emojiContainer: emojiContainer @@ -26,6 +28,8 @@ PopupMenu { property var fromAuthor: "" property var text: "" property var emojiReactionsReactedByUser: [] + property var onClickEdit: function(){} + subMenuIcons: [ { source: Qt.resolvedUrl("../../../../shared/img/copy-to-clipboard-icon"), @@ -236,6 +240,19 @@ PopupMenu { icon.height: 16 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 { text: messageContextMenu.isProfile ? //% "Send message" diff --git a/ui/app/img/edit-message.svg b/ui/app/img/edit-message.svg new file mode 100644 index 0000000000..2e6aa3c7e4 --- /dev/null +++ b/ui/app/img/edit-message.svg @@ -0,0 +1,3 @@ + + + diff --git a/ui/imports/Constants.qml b/ui/imports/Constants.qml index 943afdb757..3a40b0ba99 100644 --- a/ui/imports/Constants.qml +++ b/ui/imports/Constants.qml @@ -68,6 +68,7 @@ QtObject { readonly property int audioType: 8 readonly property int communityInviteType: 9 readonly property int gapType: 10 + readonly property int editType: 11 readonly property string watchWalletType: "watch" readonly property string keyWalletType: "key" @@ -147,6 +148,8 @@ QtObject { readonly property string ens_connected: "connected" readonly property string ens_connected_dkey: "connected-different-key" + readonly property string editLabel: ` ` + qsTr("(edited)") + `` + readonly property var ensState: { //% "Username already taken :(" "taken": qsTrId("ens-username-taken"), diff --git a/ui/imports/Themes/DarkTheme.qml b/ui/imports/Themes/DarkTheme.qml index ced04fe791..390a435d18 100644 --- a/ui/imports/Themes/DarkTheme.qml +++ b/ui/imports/Themes/DarkTheme.qml @@ -29,6 +29,7 @@ Theme { property color green: "#4EBC60" property color turquoise: "#007b7d" 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 background: "#2C2C2C" @@ -43,6 +44,7 @@ Theme { property color currentUserTextColor: white property color secondaryBackground: "#353a4d" property color inputBackground: darkGrey + property color secondaryinputBackground: fivePercentBlack property color inputBorderFocus: blue property color secondaryMenuBorder: darkGrey property color inputColor: textColor diff --git a/ui/imports/Themes/LightTheme.qml b/ui/imports/Themes/LightTheme.qml index c71fbf0640..e2e24b4eb9 100644 --- a/ui/imports/Themes/LightTheme.qml +++ b/ui/imports/Themes/LightTheme.qml @@ -29,6 +29,7 @@ Theme { property color green: "#4EBC60" property color turquoise: "#007b7d" 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 background: white @@ -43,6 +44,7 @@ Theme { property color currentUserTextColor: white property color secondaryBackground: lightBlue property color inputBackground: grey + property color secondaryinputBackground: fivePercentBlack property color inputBorderFocus: blue property color secondaryMenuBorder: grey3 property color inputColor: black diff --git a/ui/imports/Utils.qml b/ui/imports/Utils.qml index a716213ff7..bfd2ab3784 100644 --- a/ui/imports/Utils.qml +++ b/ui/imports/Utils.qml @@ -85,6 +85,10 @@ QtObject { `.emoji {` + `vertical-align: bottom;` + `}` + + `span.isEdited {` + + `color: ${Style.current.secondaryText};` + + `margin-left: 5px` + + `}` + `` + `${msg}` } diff --git a/ui/shared/status/StatusChatInput.qml b/ui/shared/status/StatusChatInput.qml index a9fd2a5aab..07e7fba43a 100644 --- a/ui/shared/status/StatusChatInput.qml +++ b/ui/shared/status/StatusChatInput.qml @@ -23,6 +23,7 @@ Rectangle { property bool isColonPressed: false; property bool isReply: false property bool isImage: false + property bool isEdit: false property var recentStickers property var stickerPackList @@ -66,8 +67,8 @@ Rectangle { anchors.left: parent.left anchors.right: parent.right - color: Style.current.background - + color: Style.current.transparent + function calculateExtraHeightFactor() { const factor = (messageInputField.length / 500) + 1; return (factor > 5) ? 5 : factor; @@ -694,7 +695,7 @@ Rectangle { anchors.leftMargin: 4 anchors.bottom: parent.bottom anchors.bottomMargin: 16 - visible: control.chatType === Constants.chatTypeOneToOne && !control.isStatusUpdateInput + visible: !isEdit && control.chatType === Constants.chatTypeOneToOne && !control.isStatusUpdateInput onClicked: { highlighted = true chatCommandsPopup.open() @@ -710,8 +711,8 @@ Rectangle { anchors.leftMargin: chatCommandsBtn.visible ? 2 : 4 anchors.bottom: parent.bottom anchors.bottomMargin: 16 - visible: control.chatType !== Constants.chatTypePublic && !control.isStatusUpdateInput - + visible: !isEdit && control.chatType !== Constants.chatTypePublic && !control.isStatusUpdateInput + onClicked: { highlighted = true imageDialog.open() @@ -739,7 +740,7 @@ Rectangle { return messageInputField.implicitHeight } - color: Style.current.inputBackground + color: isEdit ? Style.current.secondaryinputBackground : Style.current.inputBackground radius: control.isStatusUpdateInput ? 36 : height > defaultInputFieldHeight + 1 || extendedArea.visible ? 16 : 32 @@ -783,7 +784,7 @@ Rectangle { anchors.bottom: control.isStatusUpdateInput ? undefined : messageInput.top anchors.top: control.isStatusUpdateInput ? messageInput.bottom : undefined 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 Rectangle { @@ -1091,7 +1092,7 @@ Rectangle { anchors.leftMargin: 2 anchors.bottom: parent.bottom 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 type: "secondary" onClicked: {