diff --git a/src/app/chat/views/message_list.nim b/src/app/chat/views/message_list.nim
index 9559e08cf2..37f6521d02 100644
--- a/src/app/chat/views/message_list.nim
+++ b/src/app/chat/views/message_list.nim
@@ -25,6 +25,7 @@ type
ResponseTo = UserRole + 14
PlainText = UserRole + 15
Index = UserRole + 16
+ ImageUrls = UserRole + 17
QtObject:
type
@@ -82,6 +83,7 @@ QtObject:
of ChatMessageRoles.OutgoingStatus: result = newQVariant(message.outgoingStatus)
of ChatMessageRoles.ResponseTo: result = newQVariant(message.responseTo)
of ChatMessageRoles.Index: result = newQVariant(index.row)
+ of ChatMessageRoles.ImageUrls: result = newQVariant(message.imageUrls)
method roleNames(self: ChatMessageList): Table[int, string] =
{
@@ -100,7 +102,8 @@ QtObject:
ChatMessageRoles.Id.int: "messageId",
ChatMessageRoles.OutgoingStatus.int: "outgoingStatus",
ChatMessageRoles.ResponseTo.int: "responseTo",
- ChatMessageRoles.Index.int: "index"
+ ChatMessageRoles.Index.int: "index",
+ ChatMessageRoles.ImageUrls.int: "imageUrls"
}.toTable
proc getMessageIndex(self: ChatMessageList, messageId: string): int {.slot.} =
diff --git a/src/signals/messages.nim b/src/signals/messages.nim
index 675d8193c7..fe1109cdf6 100644
--- a/src/signals/messages.nim
+++ b/src/signals/messages.nim
@@ -1,4 +1,4 @@
-import json, random, sequtils, sugar
+import json, random, re, strutils, sequtils, sugar
import json_serialization
import ../status/libstatus/accounts as status_accounts
import ../status/libstatus/settings as status_settings
@@ -140,7 +140,12 @@ proc toTextItem*(jsonText: JsonNode): TextItem =
proc toMessage*(jsonMsg: JsonNode): Message =
- result = Message(
+ let
+ regex = re(r"(\b(https?|ftp):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|].(?:jpg|jpeg|gif|png|svg))", flags = {reStudy, reIgnoreCase})
+ text = jsonMsg{"text"}.getStr
+ imageUrls = findAll(text, regex)
+
+ var message = Message(
alias: jsonMsg{"alias"}.getStr,
chatId: jsonMsg{"localChatId"}.getStr,
clock: jsonMsg{"clock"}.getInt,
@@ -162,8 +167,14 @@ proc toMessage*(jsonMsg: JsonNode): Message =
outgoingStatus: $jsonMsg{"outgoingStatus"}.getStr,
isCurrentUser: $jsonMsg{"outgoingStatus"}.getStr == "sending" or $jsonMsg{"outgoingStatus"}.getStr == "sent",
stickerHash: "",
- parsedText: @[]
+ parsedText: @[],
+ imageUrls: ""
)
+
+ if imageUrls.len > 0:
+ message.imageUrls = imageUrls.join(" ")
+
+ result = message
if jsonMsg["parsedText"].kind != JNull:
for text in jsonMsg["parsedText"]:
diff --git a/src/status/chat/message.nim b/src/status/chat/message.nim
index fdd936cd4c..491637d16f 100644
--- a/src/status/chat/message.nim
+++ b/src/status/chat/message.nim
@@ -42,6 +42,7 @@ type Message* = object
isCurrentUser*: bool
stickerHash*: string
outgoingStatus*: string
+ imageUrls*: string
proc `$`*(self: Message): string =
result = fmt"Message(id:{self.id}, chatId:{self.chatId}, clock:{self.clock}, from:{self.fromAuthor}, type:{self.contentType})"
diff --git a/ui/app/AppLayouts/Chat/ChatColumn.qml b/ui/app/AppLayouts/Chat/ChatColumn.qml
index 01ec9cdb72..121984ef30 100644
--- a/ui/app/AppLayouts/Chat/ChatColumn.qml
+++ b/ui/app/AppLayouts/Chat/ChatColumn.qml
@@ -7,8 +7,10 @@ import "./components"
import "./ChatColumn"
StackLayout {
+ id: chatColumnLayout
property int chatGroupsListViewCount: 0
property bool isReply: false
+ property var appSettings
Layout.fillHeight: true
Layout.fillWidth: true
Layout.minimumWidth: 300
@@ -36,6 +38,7 @@ StackLayout {
ChatMessages {
id: chatMessages
messageList: chatsModel.messageList
+ appSettings: chatColumnLayout.appSettings
}
}
diff --git a/ui/app/AppLayouts/Chat/ChatColumn/ChatMessages.qml b/ui/app/AppLayouts/Chat/ChatColumn/ChatMessages.qml
index 2803aea7de..00af7984fc 100644
--- a/ui/app/AppLayouts/Chat/ChatColumn/ChatMessages.qml
+++ b/ui/app/AppLayouts/Chat/ChatColumn/ChatMessages.qml
@@ -11,6 +11,7 @@ ScrollView {
id: scrollView
property var messageList: MessagesData {}
+ property var appSettings
property bool loadingMessages: false
property real scrollY: chatLogView.visibleArea.yPosition * chatLogView.contentHeight
@@ -21,11 +22,23 @@ ScrollView {
ScrollBar.vertical.policy: chatLogView.contentHeight > chatLogView.height ? ScrollBar.AlwaysOn : ScrollBar.AlwaysOff
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
+ function scrollToBottom(force, caller) {
+ if (!true && !chatLogView.atYEnd) {
+ // User has scrolled up, we don't want to scroll back
+ return
+ }
+ if (caller && caller !== chatLogView.itemAtIndex(chatLogView.count - 1)) {
+ // If we have a caller, only accept its request if it's the last message
+ return
+ }
+ Qt.callLater( chatLogView.positionViewAtEnd )
+ }
+
ListView {
+ id: chatLogView
anchors.fill: parent
spacing: 4
boundsBehavior: Flickable.StopAtBounds
- id: chatLogView
Layout.fillWidth: true
Layout.fillHeight: true
@@ -36,17 +49,11 @@ ScrollView {
}
onActiveChannelChanged: {
- Qt.callLater( chatLogView.positionViewAtEnd )
+ scrollToBottom(true)
}
onMessagePushed: {
- if (!chatLogView.atYEnd) {
- // User has scrolled up, we don't want to scroll back
- return
- }
-
- if(chatLogView.atYEnd)
- Qt.callLater( chatLogView.positionViewAtEnd )
+ scrollToBottom()
}
onMessageNotificationPushed: function(chatId, msg) {
@@ -141,6 +148,8 @@ ScrollView {
}
return -1;
}
+ appSettings: scrollView.appSettings
+ scrollToBottom: scrollView.scrollToBottom
}
}
diff --git a/ui/app/AppLayouts/Chat/ChatColumn/Message.qml b/ui/app/AppLayouts/Chat/ChatColumn/Message.qml
index c2ec6ffdb3..3021c2ac1f 100644
--- a/ui/app/AppLayouts/Chat/ChatColumn/Message.qml
+++ b/ui/app/AppLayouts/Chat/ChatColumn/Message.qml
@@ -28,7 +28,7 @@ Item {
property string authorPrevMsg: "authorPrevMsg"
property bool isEmoji: contentType === Constants.emojiType
- property bool isMessage: contentType === Constants.messageType || contentType === Constants.stickerType
+ property bool isMessage: contentType === Constants.messageType || contentType === Constants.stickerType
property bool isStatusMessage: contentType === Constants.systemMessagePrivateGroupType
property bool isSticker: contentType === Constants.stickerType
@@ -37,12 +37,15 @@ Item {
property string repliedMessageContent: replyMessageIndex > -1 ? chatsModel.messageList.getMessageData(replyMessageIndex, "message") : "";
property var profileClick: function () {}
-
+ property var scrollToBottom: function () {}
+ property var appSettings
width: parent.width
+ anchors.right: !isCurrentUser ? undefined : parent.right
+ id: messageWrapper
height: {
switch(contentType){
case Constants.chatIdentifier:
- return parent.parent.height - 100
+ return channelIdentifier.height + channelIdentifier.verticalMargin
case Constants.stickerType:
return stickerId.height + 50 + (dateGroupLbl.visible ? 50 : 0)
default:
@@ -51,11 +54,11 @@ Item {
}
function linkify(inputText) {
- //URLs starting with http://, https://, or ftp://
+ // URLs starting with http://, https://, or ftp://
var replacePattern1 = /(\b(https?|ftp):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gim;
var replacedText = inputText.replace(replacePattern1, "$1");
- //URLs starting with "www." (without // before it, or it'd re-link the ones done above).
+ // URLs starting with "www." (without // before it, or it'd re-link the ones done above).
var replacePattern2 = /(^|[^\/])(www\.[\S]+(\b|$))/gim;
replacedText = replacedText.replace(replacePattern2, "$1$2");
@@ -68,10 +71,13 @@ Item {
}
Item {
+ property int verticalMargin: 50
id: channelIdentifier
visible: authorCurrentMsg == ""
anchors.horizontalCenter: parent.horizontalCenter
- anchors.verticalCenter: parent.verticalCenter
+ anchors.top: parent.top
+ anchors.topMargin: this.visible ? verticalMargin : 0
+ height: this.visible ? childrenRect.height + verticalMargin : 0
Rectangle {
id: circleId
@@ -171,7 +177,6 @@ Item {
}
}
}
-
}
// Private group Messages
@@ -363,7 +368,7 @@ Item {
StyledTextEdit {
id: chatText
- textFormat: TextEdit.RichText
+ textFormat: Text.RichText
text: {
if(contentType === Constants.stickerType) return "";
let msg = linkify(message);
@@ -468,17 +473,16 @@ Item {
let hours = messageDate.getHours();
return (hours < 10 ? "0" + hours : hours) + ":" + (minutes < 10 ? "0" + minutes : minutes)
}
- anchors.top: chatBox.bottom
+ anchors.top: messageWrapper.appSettings.displayChatImages && imageUrls != "" ? imageChatBox.bottom : chatBox.bottom
anchors.topMargin: 4
anchors.bottomMargin: Style.current.padding
- anchors.right: chatBox.right
+ anchors.right: messageWrapper.appSettings.displayChatImages && imageUrls != "" ? imageChatBox.right : chatBox.right
anchors.rightMargin: isCurrentUser ? 5 : Style.current.padding
font.pixelSize: 10
readOnly: true
selectByMouse: true
visible: (isEmoji || isMessage || isSticker)
}
-
StyledTextEdit {
id: sentMessage
@@ -496,22 +500,74 @@ Item {
readOnly: true
visible: isCurrentUser && (isEmoji || isMessage || isSticker)
}
-
- // This rectangle's only job is to mask the corner to make it less rounded... yep
+
Rectangle {
- // TODO find a way to show the corner for stickers since they have a border
- visible: isMessage || isEmoji
- color: chatBox.color
- width: 18
- height: 18
- anchors.bottom: chatBox.bottom
- anchors.bottomMargin: 0
- anchors.left: !isCurrentUser ? chatBox.left : undefined
- anchors.leftMargin: 0
- anchors.right: !isCurrentUser ? undefined : chatBox.right
- anchors.rightMargin: 0
- radius: 4
- z: -1
+ property int chatVerticalPadding: 12
+ property int chatHorizontalPadding: 12
+ property int imageWidth: 350
+
+ id: imageChatBox
+ visible: messageWrapper.appSettings.displayChatImages && imageUrls != ""
+ height: {
+ if (!imageChatBox.visible) {
+ return 0
+ }
+
+ let h = chatVerticalPadding
+ for (let i = 0; i < imageRepeater.count; i++) {
+ h += imageRepeater.itemAt(i).height
+ }
+ return h + chatVerticalPadding * imageRepeater.count
+ }
+ color: isCurrentUser ? Style.current.blue : Style.current.lightBlue
+ border.color: "transparent"
+ width: imageWidth+ 2 * chatHorizontalPadding
+ radius: 16
+ anchors.left: !isCurrentUser ? chatImage.right : undefined
+ anchors.leftMargin: !isCurrentUser ? 8 : 0
+ anchors.right: !isCurrentUser ? undefined : parent.right
+ anchors.rightMargin: !isCurrentUser ? 0 : Style.current.padding
+ anchors.top: messageWrapper.appSettings.displayChatImages && imageUrls != "" ? chatBox.bottom : chatTime.bottom
+ anchors.topMargin: Style.current.smallPadding
+
+ Repeater {
+ id: imageRepeater
+ model: messageWrapper.appSettings.displayChatImages && imageUrls != "" ? imageUrls.split(" ") : []
+
+ Image {
+ id: imageMessage
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.top: (index == 0) ? parent.top: parent.children[index-1].bottom
+ anchors.topMargin: imageChatBox.chatVerticalPadding
+ sourceSize.width: imageChatBox.imageWidth
+ source: modelData
+ onStatusChanged: {
+ if (imageMessage.status == Image.Error) {
+ imageMessage.height = 0
+ imageMessage.visible = false
+ imageChatBox.height = 0
+ imageChatBox.visible = false
+ } else if (imageMessage.status == Image.Ready) {
+ messageWrapper.scrollToBottom(true, messageWrapper)
+ }
+ }
+ }
+ }
+
+ // This rectangle's only job is to mask the corner to make it less rounded... yep
+ Rectangle {
+ color: parent.color
+ width: 18
+ height: 18
+ anchors.bottom: parent.bottom
+ anchors.bottomMargin: 0
+ anchors.left: !isCurrentUser ? parent.left : undefined
+ anchors.leftMargin: 0
+ anchors.right: !isCurrentUser ? undefined : parent.right
+ anchors.rightMargin: 0
+ radius: 4
+ z: -1
+ }
}
}
diff --git a/ui/app/AppLayouts/Chat/ChatLayout.qml b/ui/app/AppLayouts/Chat/ChatLayout.qml
index b6fce881ef..dcc01d471c 100644
--- a/ui/app/AppLayouts/Chat/ChatLayout.qml
+++ b/ui/app/AppLayouts/Chat/ChatLayout.qml
@@ -24,6 +24,7 @@ SplitView {
ChatColumn {
id: chatColumn
chatGroupsListViewCount: contactsColumn.chatGroupsListViewCount
+ appSettings: chatView.appSettings
}
}
diff --git a/ui/app/AppLayouts/Profile/ProfileLayout.qml b/ui/app/AppLayouts/Profile/ProfileLayout.qml
index 0d8c5ebc71..292c2f03a6 100644
--- a/ui/app/AppLayouts/Profile/ProfileLayout.qml
+++ b/ui/app/AppLayouts/Profile/ProfileLayout.qml
@@ -50,7 +50,9 @@ SplitView {
NotificationsContainer {}
- AdvancedContainer {}
+ AdvancedContainer {
+ appSettings: profileView.appSettings
+ }
HelpContainer {}
diff --git a/ui/app/AppLayouts/Profile/Sections/AdvancedContainer.qml b/ui/app/AppLayouts/Profile/Sections/AdvancedContainer.qml
index f85bf19f23..b1073033d7 100644
--- a/ui/app/AppLayouts/Profile/Sections/AdvancedContainer.qml
+++ b/ui/app/AppLayouts/Profile/Sections/AdvancedContainer.qml
@@ -5,6 +5,7 @@ import "../../../../imports"
import "../../../../shared"
Item {
+ property var appSettings
id: advancedContainer
width: 200
height: 200
@@ -96,6 +97,7 @@ Item {
}
RowLayout {
+ id: nodeTabSettings
anchors.top: browserTabSettings.bottom
anchors.topMargin: 20
anchors.left: parent.left
@@ -115,6 +117,25 @@ Item {
text: qsTrId("under-development")
}
}
+
+ RowLayout {
+ anchors.top: nodeTabSettings.bottom
+ anchors.topMargin: 20
+ anchors.left: parent.left
+ anchors.leftMargin: 24
+ StyledText {
+ text: qsTr("Display images in chat automatically")
+ }
+ Switch {
+ checked: appSettings.displayChatImages
+ onCheckedChanged: function(value) {
+ advancedContainer.appSettings.displayChatImages = this.checked
+ }
+ }
+ StyledText {
+ text: qsTr("under development")
+ }
+ }
}
/*##^##
diff --git a/ui/main.qml b/ui/main.qml
index 6bbe5e5817..5098c219a5 100644
--- a/ui/main.qml
+++ b/ui/main.qml
@@ -61,6 +61,7 @@ ApplicationWindow {
property var chatSplitView
property var walletSplitView
property var profileSplitView
+ property bool displayChatImages: false
}
SystemTrayIcon {