feat: display messages with an image contenttype

This commit is contained in:
Richard Ramos 2020-07-17 15:44:25 -04:00 committed by Iuri Matias
parent a9cddde37e
commit 5351fb62dc
14 changed files with 123 additions and 18 deletions

View File

@ -14,6 +14,7 @@ type
ChatType = UserRole + 6
Color = UserRole + 7
HasMentions = UserRole + 8
ContentType = UserRole + 9
QtObject:
type
@ -59,6 +60,7 @@ QtObject:
of ChannelsRoles.Name: result = newQVariant(name)
of ChannelsRoles.Timestamp: result = newQVariant($chatItem.timestamp)
of ChannelsRoles.LastMessage: result = newQVariant(self.renderBlock(chatItem.lastMessage))
of ChannelsRoles.ContentType: result = newQVariant(chatItem.lastMessage.contentType.int)
of ChannelsRoles.UnreadMessages: result = newQVariant(chatItem.unviewedMessagesCount)
of ChannelsRoles.Identicon: result = newQVariant(chatItem.identicon)
of ChannelsRoles.ChatType: result = newQVariant(chatItem.chatType.int)
@ -74,7 +76,8 @@ QtObject:
ChannelsRoles.Identicon.int: "identicon",
ChannelsRoles.ChatType.int: "chatType",
ChannelsRoles.Color.int: "color",
ChannelsRoles.HasMentions.int: "hasMentions"
ChannelsRoles.HasMentions.int: "hasMentions",
ChannelsRoles.ContentType.int: "contentType"
}.toTable
proc addChatItemToList*(self: ChannelsList, channel: Chat): int =
@ -122,7 +125,7 @@ QtObject:
else:
self.chats[0] = channel
self.dataChanged(topLeft, bottomRight, @[ChannelsRoles.Name.int, ChannelsRoles.LastMessage.int, ChannelsRoles.Timestamp.int, ChannelsRoles.UnreadMessages.int, ChannelsRoles.Identicon.int, ChannelsRoles.ChatType.int, ChannelsRoles.Color.int, ChannelsRoles.HasMentions.int])
self.dataChanged(topLeft, bottomRight, @[ChannelsRoles.Name.int, ChannelsRoles.ContentType.int, ChannelsRoles.LastMessage.int, ChannelsRoles.Timestamp.int, ChannelsRoles.UnreadMessages.int, ChannelsRoles.Identicon.int, ChannelsRoles.ChatType.int, ChannelsRoles.Color.int, ChannelsRoles.HasMentions.int])
proc clearUnreadMessagesCount*(self: ChannelsList, channel: var Chat) =
let idx = self.chats.findIndexById(channel.id)
@ -134,7 +137,7 @@ QtObject:
channel.hasMentions = false
self.chats[idx] = channel
self.dataChanged(topLeft, bottomRight, @[ChannelsRoles.Name.int, ChannelsRoles.LastMessage.int, ChannelsRoles.Timestamp.int, ChannelsRoles.UnreadMessages.int, ChannelsRoles.Identicon.int, ChannelsRoles.ChatType.int, ChannelsRoles.Color.int, ChannelsRoles.HasMentions.int])
self.dataChanged(topLeft, bottomRight, @[ChannelsRoles.Name.int, ChannelsRoles.ContentType.int, ChannelsRoles.LastMessage.int, ChannelsRoles.Timestamp.int, ChannelsRoles.UnreadMessages.int, ChannelsRoles.Identicon.int, ChannelsRoles.ChatType.int, ChannelsRoles.Color.int, ChannelsRoles.HasMentions.int])
proc renderInline(self: ChannelsList, elem: TextItem): string =
case elem.textType:

View File

@ -27,6 +27,7 @@ type
Index = UserRole + 16
ImageUrls = UserRole + 17
Timeout = UserRole + 18
Image = UserRole + 19
QtObject:
type
@ -109,6 +110,7 @@ QtObject:
of ChatMessageRoles.Index: result = newQVariant(index.row)
of ChatMessageRoles.ImageUrls: result = newQVariant(message.imageUrls)
of ChatMessageRoles.Timeout: result = newQVariant(self.timedoutMessages.contains(message.id))
of ChatMessageRoles.Image: result = newQVariant(message.image)
method roleNames(self: ChatMessageList): Table[int, string] =
{
@ -130,6 +132,7 @@ QtObject:
ChatMessageRoles.Index.int: "index",
ChatMessageRoles.ImageUrls.int: "imageUrls",
ChatMessageRoles.Timeout.int: "timeout"
ChatMessageRoles.Image.int: "image"
}.toTable
proc getMessageIndex(self: ChatMessageList, messageId: string): int {.slot.} =
@ -146,6 +149,8 @@ QtObject:
of "message": result = (message.text)
of "identicon": result = (message.identicon)
of "timestamp": result = $(message.timestamp)
of "image": result = $(message.image)
of "contentType": result = $(message.contentType.int)
else: result = ("")
proc add*(self: ChatMessageList, message: Message) =

View File

@ -1,11 +1,11 @@
import json, random, re, strutils, sequtils, sugar
import json, random, re, strutils, sequtils, sugar, chronicles
import json_serialization
import ../status/libstatus/accounts as status_accounts
import ../status/libstatus/settings as status_settings
import ../status/libstatus/types as status_types
import ../status/chat/[chat, message]
import ../status/profile/[profile, devices]
import types
import types
proc toMessage*(jsonMsg: JsonNode): Message
@ -141,12 +141,18 @@ proc toTextItem*(jsonText: JsonNode): TextItem =
proc toMessage*(jsonMsg: JsonNode): Message =
var contentType: ContentType
try:
contentType = ContentType(jsonMsg{"contentType"}.getInt)
except:
warn "Unknown content type received", type = jsonMsg{"contentType"}.getInt
contentType = ContentType.Unknown
var message = Message(
alias: jsonMsg{"alias"}.getStr,
chatId: jsonMsg{"localChatId"}.getStr,
clock: jsonMsg{"clock"}.getInt,
contentType: ContentType(jsonMsg{"contentType"}.getInt),
contentType: contentType,
ensName: jsonMsg{"ensName"}.getStr,
fromAuthor: jsonMsg{"from"}.getStr,
id: jsonMsg{"id"}.getStr,
@ -165,7 +171,8 @@ proc toMessage*(jsonMsg: JsonNode): Message =
isCurrentUser: $jsonMsg{"outgoingStatus"}.getStr == "sending" or $jsonMsg{"outgoingStatus"}.getStr == "sent",
stickerHash: "",
parsedText: @[],
imageUrls: ""
imageUrls: "",
image: $jsonMsg{"image"}.getStr
)
if jsonMsg["parsedText"].kind != JNull:

View File

@ -8,7 +8,8 @@ type ContentType* {.pure.} = enum
Status = 3,
Emoji = 4,
Transaction = 5,
Group = 6
Group = 6,
Image = 7
type TextItem* = object
textType*: string
@ -43,6 +44,8 @@ type Message* = object
stickerHash*: string
outgoingStatus*: string
imageUrls*: string
image*: string
proc `$`*(self: Message): string =
result = fmt"Message(id:{self.id}, chatId:{self.chatId}, clock:{self.clock}, from:{self.fromAuthor}, type:{self.contentType})"

View File

@ -25,6 +25,7 @@ Item {
property string authorPrevMsg: "authorPrevMsg"
property bool isEmoji: contentType === Constants.emojiType
property bool isImage: contentType === Constants.imageType
property bool isMessage: contentType === Constants.messageType || contentType === Constants.stickerType
property bool isStatusMessage: contentType === Constants.systemMessagePrivateGroupType
property bool isSticker: contentType === Constants.stickerType
@ -34,6 +35,8 @@ Item {
property int replyMessageIndex: chatsModel.messageList.getMessageIndex(responseTo);
property string repliedMessageAuthor: replyMessageIndex > -1 ? chatsModel.messageList.getMessageData(replyMessageIndex, "userName") : "";
property string repliedMessageContent: replyMessageIndex > -1 ? chatsModel.messageList.getMessageData(replyMessageIndex, "message") : "";
property int repliedMessageType: replyMessageIndex > -1 ? parseInt(chatsModel.messageList.getMessageData(replyMessageIndex, "contentType")) : 0;
property string repliedMessageImage: replyMessageIndex > -1 ? chatsModel.messageList.getMessageData(replyMessageIndex, "image") : "";
property var profileClick: function () {}
property var scrollToBottom: function () {}

View File

@ -0,0 +1,37 @@
import QtQuick 2.3
import "../../../../../shared"
import "../../../../../imports"
Rectangle {
property int chatVerticalPadding: 12
property int chatHorizontalPadding: 12
property int imageWidth: 250
property string imageSource: "data:image/jpeg;base64,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"
id: imageChatBox
height: imageMessage.paintedHeight
width: imageMessage.paintedWidth + 2 * chatHorizontalPadding
radius: 16
visible: isImage
color: "transparent"
Image {
id: imageMessage
source: imageSource
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
anchors.topMargin: imageChatBox.chatVerticalPadding
fillMode: Image.PreserveAspectFit
width: sourceSize.width > imageWidth ? imageWidth : sourceSize.width
onStatusChanged: {
if (imageMessage.status == Image.Error) {
imageMessage.height = 0
imageMessage.visible = false
imageChatBox.height = 0
imageChatBox.visible = false
} else if (imageMessage.status == Image.Ready) {
messageItem.scrollToBottom(true, messageItem)
}
}
}
}

View File

@ -10,7 +10,7 @@ Rectangle {
color: Style.current.lightBlue
visible: responseTo != "" && replyMessageIndex > -1
// childrenRect.height shows a binding loop for soem reason, so we use heights instead
height: this.visible ? lblReplyAuthor.height + lblReplyMessage.height + 5 + 8 : 0
height: this.visible ? lblReplyAuthor.height + ((repliedMessageType == Constants.imageType ? imgReplyImage.height : lblReplyMessage.height) + 5 + 8) : 0
StyledTextEdit {
id: lblReplyAuthor
@ -23,8 +23,20 @@ Rectangle {
anchors.right: parent.right
}
ChatImage {
id: imgReplyImage
visible: repliedMessageType == Constants.imageType
imageWidth: 50
imageSource: repliedMessageImage
anchors.top: lblReplyAuthor.bottom
anchors.topMargin: 5
anchors.left: parent.left
chatHorizontalPadding: 0
}
StyledTextEdit {
id: lblReplyMessage
visible: repliedMessageType != Constants.imageType
anchors.top: lblReplyAuthor.bottom
anchors.topMargin: 5
text: Emoji.parse(Utils.linkifyAndXSS(repliedMessageContent), "26x26");
@ -39,8 +51,8 @@ Rectangle {
}
Separator {
anchors.top: lblReplyMessage.bottom
anchors.topMargin: 8
anchors.top: repliedMessageType == Constants.imageType ? imgReplyImage.bottom : lblReplyMessage.bottom
anchors.topMargin: repliedMessageType == Constants.imageType ? 15 : 8
anchors.left: lblReplyMessage.left
anchors.right: lblReplyMessage.right
anchors.rightMargin: chatTextItem.chatHorizontalPadding

View File

@ -4,7 +4,7 @@ import "../../../../../imports"
StyledTextEdit {
id: chatTime
visible: (isEmoji || isMessage || isSticker)
visible: (isEmoji || isMessage || isSticker || isImage)
color: Style.current.darkGrey
text: {
let messageDate = new Date(Math.floor(timestamp))

View File

@ -57,6 +57,13 @@ Item {
anchors.rightMargin: chatTextItem.chatHorizontalPadding
}
ChatImage {
id: chatImageContent
imageWidth: 200
imageSource: image
anchors.left: chatImage.right
}
Rectangle {
id: stickerContainer
visible: contentType === Constants.stickerType

View File

@ -18,7 +18,7 @@ Item {
UserImage {
id: chatImage
visible: (isMessage || isEmoji) && authorCurrentMsg != authorPrevMsg && !isCurrentUser
visible: (isMessage || isEmoji || isImage) && authorCurrentMsg != authorPrevMsg && !isCurrentUser
anchors.left: parent.left
anchors.leftMargin: Style.current.padding
anchors.top: dateGroupLbl.visible ? dateGroupLbl.bottom : parent.top
@ -27,7 +27,7 @@ Item {
UsernameLabel {
id: chatName
visible: (isMessage || isEmoji) && authorCurrentMsg != authorPrevMsg && !isCurrentUser
visible: (isMessage || isEmoji || isImage) && authorCurrentMsg != authorPrevMsg && !isCurrentUser
text: userName
anchors.leftMargin: 20
anchors.top: dateGroupLbl.visible ? dateGroupLbl.bottom : parent.top
@ -45,11 +45,25 @@ Item {
color: isSticker ? Style.current.background : (isCurrentUser ? Style.current.blue : Style.current.secondaryBackground)
border.color: isSticker ? Style.current.border : Style.current.transparent
border.width: 1
height: (3 * chatVerticalPadding) + (contentType == Constants.stickerType ? stickerId.height : (chatText.height + chatReply.height))
height: {
let h = (3 * chatVerticalPadding)
switch(contentType){
case Constants.stickerType:
h += stickerId.height;
break;
default:
h += chatText.visible ? chatText.height : 0;
h += chatImageContent.visible ? chatImageContent.height: 0;
h += chatReply.visible ? chatReply.height : 0;
}
return h;
}
width: {
switch(contentType){
case Constants.stickerType:
return stickerId.width + (2 * chatBox.chatHorizontalPadding);
case Constants.imageType:
return chatImageContent.width
default:
if (longChatText || longReply) {
return 400;
@ -69,7 +83,7 @@ Item {
anchors.rightMargin: !isCurrentUser ? 0 : Style.current.padding
anchors.top: authorCurrentMsg != authorPrevMsg && !isCurrentUser ? chatImage.top : (dateGroupLbl.visible ? dateGroupLbl.bottom : parent.top)
anchors.topMargin: 0
visible: isMessage || isEmoji
visible: isMessage || isEmoji || isImage
ChatReply {
id: chatReply
@ -96,6 +110,12 @@ Item {
color: !isCurrentUser ? Style.current.textColor : Style.current.currentUserTextColor
}
ChatImage {
id: chatImageContent
imageWidth: 250
imageSource: image
}
Sticker {
id: stickerId
anchors.left: parent.left

View File

@ -4,7 +4,7 @@ import "../../../../../imports"
StyledText {
id: sentMessage
visible: isCurrentUser && !timeout && !isExpired && (isEmoji || isMessage || isSticker)
visible: isCurrentUser && !timeout && (isEmoji || isMessage || isSticker || isImage)
color: Style.current.darkGrey
text: outgoingStatus == "sent" ?
//% "Sent"

View File

@ -13,6 +13,7 @@ Rectangle {
property int chatType: Constants.chatTypePublic
property string searchStr: ""
property bool isCompact: appSettings.compactMode
property int contentType: 1
id: wrapper
color: ListView.isCurrentItem ? Style.current.secondaryBackground : Style.current.transparent
@ -80,7 +81,12 @@ Rectangle {
id: lastChatMessage
visible: !isCompact
//% "No messages"
text: lastMessage ? Emoji.parse(lastMessage, "26x26").replace(/\n|\r/g, ' ') : qsTrId("no-messages")
text: {
if(contentType == Constants.imageType){
return qsTr("Image");
}
return lastMessage ? Emoji.parse(lastMessage, "26x26").replace(/\n|\r/g, ' ') : qsTrId("no-messages")
}
clip: true // This is needed because emojis don't ellide correctly
anchors.right: contactNumberChatsCircle.left
anchors.rightMargin: Style.current.smallPadding

View File

@ -28,6 +28,7 @@ Item {
chatType: model.chatType
unviewedMessagesCount: model.unviewedMessagesCount
hasMentions: model.hasMentions
contentType: model.contentType
searchStr: chatGroupsContainer.searchStr
}
onCountChanged: {

View File

@ -15,6 +15,7 @@ QtObject {
readonly property int emojiType: 4
readonly property int transactionType: 5
readonly property int systemMessagePrivateGroupType: 6
readonly property int imageType: 7
readonly property string watchWalletType: "watch"
readonly property string keyWalletType: "key"