feat: refactor Message and add Compact message type
This commit is contained in:
parent
a0c5f8624c
commit
5951fcf131
|
@ -122,6 +122,10 @@ ScrollView {
|
||||||
}
|
}
|
||||||
model: messageList
|
model: messageList
|
||||||
|
|
||||||
|
ProfilePopup {
|
||||||
|
id: profilePopup
|
||||||
|
}
|
||||||
|
|
||||||
delegate: Message {
|
delegate: Message {
|
||||||
id: msgDelegate
|
id: msgDelegate
|
||||||
fromAuthor: model.fromAuthor
|
fromAuthor: model.fromAuthor
|
||||||
|
@ -152,7 +156,6 @@ ScrollView {
|
||||||
scrollToBottom: scrollView.scrollToBottom
|
scrollToBottom: scrollView.scrollToBottom
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*##^##
|
/*##^##
|
||||||
|
|
|
@ -1,11 +1,7 @@
|
||||||
import QtQuick 2.3
|
import QtQuick 2.3
|
||||||
import QtQuick.Controls 2.3
|
|
||||||
import QtQuick.Controls 2.12
|
|
||||||
import QtQuick.Layouts 1.3
|
|
||||||
import Qt.labs.platform 1.1
|
|
||||||
import "../../../../shared"
|
import "../../../../shared"
|
||||||
import "../../../../shared/xss.js" as XSS
|
|
||||||
import "../../../../imports"
|
import "../../../../imports"
|
||||||
|
import "./MessageComponents"
|
||||||
import "../components"
|
import "../components"
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
|
@ -39,147 +35,52 @@ Item {
|
||||||
property var profileClick: function () {}
|
property var profileClick: function () {}
|
||||||
property var scrollToBottom: function () {}
|
property var scrollToBottom: function () {}
|
||||||
property var appSettings
|
property var appSettings
|
||||||
|
|
||||||
|
property bool isCompact: true
|
||||||
|
|
||||||
|
id: messageItem
|
||||||
width: parent.width
|
width: parent.width
|
||||||
anchors.right: !isCurrentUser ? undefined : parent.right
|
anchors.right: !isCurrentUser ? undefined : parent.right
|
||||||
id: messageWrapper
|
|
||||||
height: {
|
height: {
|
||||||
switch(contentType) {
|
switch(contentType) {
|
||||||
case Constants.chatIdentifier:
|
case Constants.chatIdentifier:
|
||||||
return channelIdentifier.height + channelIdentifier.verticalMargin
|
return childrenRect.height + 50
|
||||||
case Constants.stickerType:
|
default: return childrenRect.height
|
||||||
return stickerId.height + 50 + (dateGroupLbl.visible ? 50 : 0)
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function clickMessage() {
|
||||||
|
SelectedMessage.set(messageId, fromAuthor);
|
||||||
|
profileClick(userName, fromAuthor, identicon);
|
||||||
|
messageContextMenu.popup()
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
active :true
|
||||||
|
width: parent.width
|
||||||
|
// TODO get prop from settings
|
||||||
|
sourceComponent: {
|
||||||
|
switch(contentType) {
|
||||||
|
case Constants.chatIdentifier:
|
||||||
|
return channelIdentifierComponent
|
||||||
|
case Constants.systemMessagePrivateGroupType:
|
||||||
|
return channelIdentifierComponent
|
||||||
default:
|
default:
|
||||||
return childrenRect.height
|
return isCompact ? compactMessageComponent : messageComponent
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function linkify(inputText) {
|
|
||||||
// URLs starting with http://, https://, or ftp://
|
|
||||||
var replacePattern1 = /(\b(https?|ftp):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gim;
|
|
||||||
var replacedText = inputText.replace(replacePattern1, "<a href='$1'>$1</a>");
|
|
||||||
|
|
||||||
// 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<a href='http://$2'>$2</a>");
|
|
||||||
|
|
||||||
replacedText = XSS.filterXSS(replacedText)
|
|
||||||
return replacedText;
|
|
||||||
}
|
|
||||||
|
|
||||||
ProfilePopup {
|
|
||||||
id: profilePopup
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
property int verticalMargin: 50
|
|
||||||
id: channelIdentifier
|
|
||||||
visible: authorCurrentMsg == ""
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.topMargin: this.visible ? verticalMargin : 0
|
|
||||||
height: this.visible ? childrenRect.height + verticalMargin : 0
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: circleId
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
width: 120
|
|
||||||
height: 120
|
|
||||||
radius: 120
|
|
||||||
border.width: chatsModel.activeChannel.chatType == Constants.chatTypeOneToOne ? 2 : 0
|
|
||||||
border.color: Style.current.grey
|
|
||||||
color: {
|
|
||||||
if (chatsModel.activeChannel.chatType == Constants.chatTypeOneToOne) {
|
|
||||||
return Style.current.transparent
|
|
||||||
}
|
|
||||||
return chatsModel.activeChannel.color
|
|
||||||
}
|
|
||||||
|
|
||||||
Image {
|
|
||||||
visible: chatsModel.activeChannel.chatType == Constants.chatTypeOneToOne
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
width: 120
|
|
||||||
height: 120
|
|
||||||
fillMode: Image.PreserveAspectFit
|
|
||||||
source: chatsModel.activeChannel.identicon
|
|
||||||
mipmap: true
|
|
||||||
smooth: false
|
|
||||||
antialiasing: true
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
visible: chatsModel.activeChannel.chatType != Constants.chatTypeOneToOne
|
|
||||||
text: (chatsModel.activeChannel.name.charAt(0) == "#" ? chatsModel.activeChannel.name.charAt(1) : chatsModel.activeChannel.name.charAt(0)).toUpperCase()
|
|
||||||
opacity: 0.7
|
|
||||||
font.weight: Font.Bold
|
|
||||||
font.pixelSize: 51
|
|
||||||
color: "white"
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
id: channelName
|
|
||||||
wrapMode: Text.Wrap
|
|
||||||
text: {
|
|
||||||
if (chatsModel.activeChannel.chatType != Constants.chatTypePublic) {
|
|
||||||
return chatsModel.activeChannel.name;
|
|
||||||
} else {
|
|
||||||
return "#" + chatsModel.activeChannel.name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
font.weight: Font.Bold
|
|
||||||
font.pixelSize: 22
|
|
||||||
color: Style.current.textColor
|
|
||||||
anchors.top: circleId.bottom
|
|
||||||
anchors.topMargin: 16
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
visible: chatsModel.activeChannel.chatType == Constants.chatTypePrivateGroupChat && !chatsModel.activeChannel.isMember(profileModel.profile.pubKey)
|
|
||||||
anchors.top: channelName.bottom
|
|
||||||
anchors.topMargin: 16
|
|
||||||
id: joinOrDecline
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
id: joinChat
|
|
||||||
//% "Join chat"
|
|
||||||
text: qsTrId("join-chat")
|
|
||||||
font.pixelSize: 20
|
|
||||||
color: Style.current.blue
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
|
||||||
anchors.fill: parent
|
|
||||||
onClicked: {
|
|
||||||
chatsModel.joinGroup()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
Component {
|
||||||
//% "Decline invitation"
|
id: channelIdentifierComponent
|
||||||
text: qsTrId("group-chat-decline-invitation")
|
ChannelIdentifier {
|
||||||
font.pixelSize: 20
|
authorCurrentMsg: messageItem.authorCurrentMsg
|
||||||
color: Style.current.blue
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
anchors.top: joinChat.bottom
|
|
||||||
anchors.topMargin: Style.current.padding
|
|
||||||
MouseArea {
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
|
||||||
anchors.fill: parent
|
|
||||||
onClicked: {
|
|
||||||
chatsModel.leaveActiveChat()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Private group Messages
|
// Private group Messages
|
||||||
|
Component {
|
||||||
|
id: privateGroupHeaderComponent
|
||||||
StyledText {
|
StyledText {
|
||||||
wrapMode: Text.Wrap
|
wrapMode: Text.Wrap
|
||||||
text: message
|
text: message
|
||||||
|
@ -191,382 +92,21 @@ Item {
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
textFormat: Text.RichText
|
textFormat: Text.RichText
|
||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
|
||||||
id: dateGroupLbl
|
|
||||||
font.pixelSize: 13
|
|
||||||
color: Style.current.darkGrey
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
text: {
|
|
||||||
if (prevMessageIndex == -1) return ""; // identifier
|
|
||||||
|
|
||||||
let now = new Date()
|
|
||||||
let yesterday = new Date()
|
|
||||||
yesterday.setDate(now.getDate()-1)
|
|
||||||
|
|
||||||
let prevMsgTimestamp = chatsModel.messageList.getMessageData(prevMessageIndex, "timestamp")
|
|
||||||
var currentMsgDate = new Date(parseInt(timestamp, 10));
|
|
||||||
var prevMsgDate = prevMsgTimestamp === "" ? new Date(0) : new Date(parseInt(prevMsgTimestamp, 10));
|
|
||||||
if(currentMsgDate.getDay() !== prevMsgDate.getDay()){
|
|
||||||
if (now.toDateString() === currentMsgDate.toDateString()) {
|
|
||||||
return qsTr("Today")
|
|
||||||
} else if (yesterday.toDateString() === currentMsgDate.toDateString()) {
|
|
||||||
//% "Yesterday"
|
|
||||||
return qsTrId("yesterday")
|
|
||||||
} else {
|
|
||||||
const monthNames = [
|
|
||||||
qsTr("January"),
|
|
||||||
qsTr("February"),
|
|
||||||
qsTr("March"),
|
|
||||||
qsTr("April"),
|
|
||||||
qsTr("May"),
|
|
||||||
qsTr("June"),
|
|
||||||
qsTr("July"),
|
|
||||||
qsTr("August"),
|
|
||||||
qsTr("September"),
|
|
||||||
qsTr("October"),
|
|
||||||
qsTr("November"),
|
|
||||||
qsTr("December")
|
|
||||||
];
|
|
||||||
return monthNames[currentMsgDate.getMonth()] + ", " + currentMsgDate.getDay()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return "";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
// Normal message
|
||||||
anchors.top: parent.top
|
Component {
|
||||||
anchors.topMargin: 20
|
id: messageComponent
|
||||||
visible: text !== ""
|
NormalMessage {
|
||||||
}
|
clickMessage: messageItem.clickMessage
|
||||||
|
|
||||||
// Messages
|
|
||||||
Image {
|
|
||||||
id: chatImage
|
|
||||||
width: 36
|
|
||||||
height: 36
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.leftMargin: Style.current.padding
|
|
||||||
anchors.top: dateGroupLbl.visible ? dateGroupLbl.bottom : parent.top
|
|
||||||
anchors.topMargin: 20
|
|
||||||
fillMode: Image.PreserveAspectFit
|
|
||||||
source: identicon
|
|
||||||
visible: (isMessage || isEmoji) && authorCurrentMsg != authorPrevMsg && !isCurrentUser
|
|
||||||
mipmap: true
|
|
||||||
smooth: false
|
|
||||||
antialiasing: true
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
|
||||||
anchors.fill: parent
|
|
||||||
onClicked: {
|
|
||||||
SelectedMessage.set(messageId, fromAuthor);
|
|
||||||
profileClick(userName, fromAuthor, identicon);
|
|
||||||
messageContextMenu.popup()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
StyledTextEdit {
|
// Compact Messages
|
||||||
id: chatName
|
Component {
|
||||||
text: userName
|
id: compactMessageComponent
|
||||||
anchors.leftMargin: 20
|
CompactMessage {
|
||||||
anchors.top: dateGroupLbl.visible ? dateGroupLbl.bottom : parent.top
|
clickMessage: messageItem.clickMessage
|
||||||
anchors.topMargin: 0
|
|
||||||
anchors.left: chatImage.right
|
|
||||||
font.bold: true
|
|
||||||
font.pixelSize: 14
|
|
||||||
readOnly: true
|
|
||||||
wrapMode: Text.WordWrap
|
|
||||||
selectByMouse: true
|
|
||||||
visible: (isMessage || isEmoji) && authorCurrentMsg != authorPrevMsg && !isCurrentUser
|
|
||||||
MouseArea {
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
|
||||||
anchors.fill: parent
|
|
||||||
onClicked: {
|
|
||||||
SelectedMessage.set(messageId, fromAuthor);
|
|
||||||
profileClick(userName, fromAuthor, identicon)
|
|
||||||
messageContextMenu.popup()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
property int chatVerticalPadding: 7
|
|
||||||
property int chatHorizontalPadding: 12
|
|
||||||
|
|
||||||
id: chatBox
|
|
||||||
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))
|
|
||||||
width: {
|
|
||||||
switch(contentType){
|
|
||||||
case Constants.stickerType:
|
|
||||||
return stickerId.width + (2 * chatBox.chatHorizontalPadding);
|
|
||||||
default:
|
|
||||||
return plainText.length > 54 ? 400 : chatText.width + 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: authorCurrentMsg != authorPrevMsg && !isCurrentUser ? chatImage.top : (dateGroupLbl.visible ? dateGroupLbl.bottom : parent.top)
|
|
||||||
anchors.topMargin: 0
|
|
||||||
visible: isMessage || isEmoji
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: chatReply
|
|
||||||
color: isCurrentUser ? Style.current.blue : Style.current.lightBlue
|
|
||||||
visible: responseTo != "" && replyMessageIndex > -1
|
|
||||||
height: chatReply.visible ? childrenRect.height : 0
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.topMargin: chatReply.visible ? chatBox.chatVerticalPadding : 0
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.leftMargin: Style.current.padding
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.rightMargin: chatBox.chatHorizontalPadding
|
|
||||||
|
|
||||||
StyledTextEdit {
|
|
||||||
id: lblReplyAuthor
|
|
||||||
text: "↳" + repliedMessageAuthor
|
|
||||||
color: Style.current.darkGrey
|
|
||||||
readOnly: true
|
|
||||||
selectByMouse: true
|
|
||||||
wrapMode: Text.Wrap
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledTextEdit {
|
|
||||||
id: lblReplyMessage
|
|
||||||
anchors.top: lblReplyAuthor.bottom
|
|
||||||
anchors.topMargin: 5
|
|
||||||
text: Emoji.parse(linkify(repliedMessageContent), "26x26");
|
|
||||||
textFormat: Text.RichText
|
|
||||||
color: Style.current.darkGrey
|
|
||||||
readOnly: true
|
|
||||||
selectByMouse: true
|
|
||||||
wrapMode: Text.Wrap
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
}
|
|
||||||
|
|
||||||
Separator {
|
|
||||||
anchors.top: lblReplyMessage.bottom
|
|
||||||
anchors.topMargin: 8
|
|
||||||
anchors.left: lblReplyMessage.left
|
|
||||||
anchors.right: lblReplyMessage.right
|
|
||||||
anchors.rightMargin: chatBox.chatHorizontalPadding
|
|
||||||
color: Style.current.darkGrey
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledTextEdit {
|
|
||||||
id: chatText
|
|
||||||
textFormat: Text.RichText
|
|
||||||
text: {
|
|
||||||
if(contentType === Constants.stickerType) return "";
|
|
||||||
let msg = linkify(message);
|
|
||||||
if(isEmoji){
|
|
||||||
return Emoji.parse(msg, "72x72");
|
|
||||||
} else {
|
|
||||||
return `<html>
|
|
||||||
<head>
|
|
||||||
<style type="text/css">
|
|
||||||
code {
|
|
||||||
background-color: #1a356b;
|
|
||||||
color: #FFFFFF;
|
|
||||||
white-space: pre;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
white-space: pre;
|
|
||||||
}
|
|
||||||
|
|
||||||
a.mention {
|
|
||||||
color: ${isCurrentUser ? Style.current.black : Style.current.white}
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
blockquote {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
${Emoji.parse(msg, "26x26")}
|
|
||||||
</body>
|
|
||||||
</html>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.leftMargin: parent.chatHorizontalPadding
|
|
||||||
anchors.right: plainText.length > 52 ? parent.right : undefined
|
|
||||||
anchors.rightMargin: plainText.length > 52 ? parent.chatHorizontalPadding : 0
|
|
||||||
horizontalAlignment: !isCurrentUser ? Text.AlignLeft : Text.AlignRight
|
|
||||||
wrapMode: Text.Wrap
|
|
||||||
anchors.top: chatReply.bottom
|
|
||||||
anchors.topMargin: chatBox.chatVerticalPadding
|
|
||||||
font.pixelSize: 15
|
|
||||||
readOnly: true
|
|
||||||
selectByMouse: true
|
|
||||||
color: !isCurrentUser ? Style.current.textColor : Style.current.currentUserTextColor
|
|
||||||
visible: contentType == Constants.messageType || isEmoji
|
|
||||||
}
|
|
||||||
|
|
||||||
Image {
|
|
||||||
id: stickerId
|
|
||||||
horizontalAlignment: !isCurrentUser ? Text.AlignLeft : Text.AlignRight
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.leftMargin: parent.chatHorizontalPadding
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.topMargin: chatBox.chatVerticalPadding
|
|
||||||
width: 140
|
|
||||||
height: 140
|
|
||||||
source: contentType === Constants.stickerType ? ("https://ipfs.infura.io/ipfs/" + sticker) : ""
|
|
||||||
visible: contentType === Constants.stickerType
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent
|
|
||||||
cursorShape: chatText.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
|
|
||||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
|
||||||
onClicked: {
|
|
||||||
if(mouse.button & Qt.RightButton) {
|
|
||||||
SelectedMessage.set(messageId, fromAuthor);
|
|
||||||
profileClick(userName, fromAuthor, identicon);
|
|
||||||
messageContextMenu.popup()
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let link = chatText.hoveredLink;
|
|
||||||
if(link.startsWith("#")){
|
|
||||||
chatsModel.joinChat(link.substring(1), Constants.chatTypePublic);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (link.startsWith('//')) {
|
|
||||||
let pk = link.replace("//", "");
|
|
||||||
profileClick(chatsModel.userNameOrAlias(pk), pk, chatsModel.generateIdenticon(pk))
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Qt.openUrlExternally(link)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledTextEdit {
|
|
||||||
id: chatTime
|
|
||||||
color: Style.current.darkGrey
|
|
||||||
text: {
|
|
||||||
let messageDate = new Date(Math.floor(timestamp))
|
|
||||||
let minutes = messageDate.getMinutes();
|
|
||||||
let hours = messageDate.getHours();
|
|
||||||
return (hours < 10 ? "0" + hours : hours) + ":" + (minutes < 10 ? "0" + minutes : minutes)
|
|
||||||
}
|
|
||||||
anchors.top: messageWrapper.appSettings.displayChatImages && imageUrls != "" ? imageChatBox.bottom : chatBox.bottom
|
|
||||||
anchors.topMargin: 4
|
|
||||||
anchors.bottomMargin: Style.current.padding
|
|
||||||
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
|
|
||||||
color: Style.current.darkGrey
|
|
||||||
text: outgoingStatus == "sent" ?
|
|
||||||
//% "Sent"
|
|
||||||
qsTrId("status-sent") :
|
|
||||||
//% "Sending..."
|
|
||||||
qsTrId("sending")
|
|
||||||
anchors.top: chatTime.top
|
|
||||||
anchors.bottomMargin: Style.current.padding
|
|
||||||
anchors.right: chatTime.left
|
|
||||||
anchors.rightMargin: 5
|
|
||||||
font.pixelSize: 10
|
|
||||||
readOnly: true
|
|
||||||
visible: isCurrentUser && (isEmoji || isMessage || isSticker)
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,120 @@
|
||||||
|
import QtQuick 2.3
|
||||||
|
import "../../../../../shared"
|
||||||
|
import "../../../../../imports"
|
||||||
|
|
||||||
|
Item {
|
||||||
|
property string authorCurrentMsg: "authorCurrentMsg"
|
||||||
|
property int verticalMargin: 50
|
||||||
|
|
||||||
|
id: channelIdentifier
|
||||||
|
visible: authorCurrentMsg == ""
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.topMargin: this.visible ? verticalMargin : 0
|
||||||
|
height: this.visible ? childrenRect.height + verticalMargin : 0
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: circleId
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
width: 120
|
||||||
|
height: 120
|
||||||
|
radius: 120
|
||||||
|
border.width: chatsModel.activeChannel.chatType === Constants.chatTypeOneToOne ? 2 : 0
|
||||||
|
border.color: Style.current.grey
|
||||||
|
color: {
|
||||||
|
if (chatsModel.activeChannel.chatType === Constants.chatTypeOneToOne) {
|
||||||
|
return Style.current.transparent
|
||||||
|
}
|
||||||
|
return chatsModel.activeChannel.color
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
visible: chatsModel.activeChannel.chatType === Constants.chatTypeOneToOne
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
width: 120
|
||||||
|
height: 120
|
||||||
|
fillMode: Image.PreserveAspectFit
|
||||||
|
source: chatsModel.activeChannel.identicon
|
||||||
|
mipmap: true
|
||||||
|
smooth: false
|
||||||
|
antialiasing: true
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
visible: chatsModel.activeChannel.chatType !== Constants.chatTypeOneToOne
|
||||||
|
text: (chatsModel.activeChannel.name.charAt(0) === "#" ? chatsModel.activeChannel.name.charAt(1) : chatsModel.activeChannel.name.charAt(0)).toUpperCase()
|
||||||
|
opacity: 0.7
|
||||||
|
font.weight: Font.Bold
|
||||||
|
font.pixelSize: 51
|
||||||
|
color: Style.current.white
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
id: channelName
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
text: {
|
||||||
|
if (chatsModel.activeChannel.chatType !== Constants.chatTypePublic) {
|
||||||
|
return chatsModel.activeChannel.name;
|
||||||
|
} else {
|
||||||
|
return "#" + chatsModel.activeChannel.name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
font.weight: Font.Bold
|
||||||
|
font.pixelSize: 22
|
||||||
|
color: Style.current.black
|
||||||
|
anchors.top: circleId.bottom
|
||||||
|
anchors.topMargin: 16
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
visible: chatsModel.activeChannel.chatType === Constants.chatTypePrivateGroupChat && !chatsModel.activeChannel.isMember(profileModel.profile.pubKey)
|
||||||
|
anchors.top: channelName.bottom
|
||||||
|
anchors.topMargin: 16
|
||||||
|
id: joinOrDecline
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
id: joinChat
|
||||||
|
//% "Join chat"
|
||||||
|
text: qsTrId("join-chat")
|
||||||
|
font.pixelSize: 20
|
||||||
|
color: Style.current.blue
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
anchors.fill: parent
|
||||||
|
onClicked: {
|
||||||
|
chatsModel.joinGroup()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
//% "Decline invitation"
|
||||||
|
text: qsTrId("group-chat-decline-invitation")
|
||||||
|
font.pixelSize: 20
|
||||||
|
color: Style.current.blue
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
anchors.top: joinChat.bottom
|
||||||
|
anchors.topMargin: Style.current.padding
|
||||||
|
MouseArea {
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
anchors.fill: parent
|
||||||
|
onClicked: {
|
||||||
|
chatsModel.leaveActiveChat()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*##^##
|
||||||
|
Designer {
|
||||||
|
D{i:0;autoSize:true;formeditorZoom:0.5;height:480;width:640}
|
||||||
|
}
|
||||||
|
##^##*/
|
|
@ -0,0 +1,45 @@
|
||||||
|
import QtQuick 2.3
|
||||||
|
import "../../../../../shared"
|
||||||
|
import "../../../../../imports"
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: chatReply
|
||||||
|
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
|
||||||
|
|
||||||
|
StyledTextEdit {
|
||||||
|
id: lblReplyAuthor
|
||||||
|
text: "↳" + repliedMessageAuthor
|
||||||
|
color: Style.current.darkGrey
|
||||||
|
readOnly: true
|
||||||
|
selectByMouse: true
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledTextEdit {
|
||||||
|
id: lblReplyMessage
|
||||||
|
anchors.top: lblReplyAuthor.bottom
|
||||||
|
anchors.topMargin: 5
|
||||||
|
text: Emoji.parse(Utils.linkifyAndXSS(repliedMessageContent), "26x26");
|
||||||
|
textFormat: Text.RichText
|
||||||
|
color: Style.current.darkGrey
|
||||||
|
readOnly: true
|
||||||
|
selectByMouse: true
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {
|
||||||
|
anchors.top: lblReplyMessage.bottom
|
||||||
|
anchors.topMargin: 8
|
||||||
|
anchors.left: lblReplyMessage.left
|
||||||
|
anchors.right: lblReplyMessage.right
|
||||||
|
anchors.rightMargin: chatTextItem.chatHorizontalPadding
|
||||||
|
color: Style.current.darkGrey
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
import QtQuick 2.3
|
||||||
|
import "../../../../../shared"
|
||||||
|
import "../../../../../imports"
|
||||||
|
|
||||||
|
StyledTextEdit {
|
||||||
|
id: chatText
|
||||||
|
visible: contentType == Constants.messageType || isEmoji
|
||||||
|
textFormat: Text.RichText
|
||||||
|
text: {
|
||||||
|
if(contentType === Constants.stickerType) return "";
|
||||||
|
let msg = Utils.linkifyAndXSS(message);
|
||||||
|
if(isEmoji){
|
||||||
|
return Emoji.parse(msg, "72x72");
|
||||||
|
} else {
|
||||||
|
return `<html>
|
||||||
|
<head>
|
||||||
|
<style type="text/css">
|
||||||
|
code {
|
||||||
|
background-color: #1a356b;
|
||||||
|
color: #FFFFFF;
|
||||||
|
white-space: pre;
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
white-space: pre;
|
||||||
|
}
|
||||||
|
a.mention {
|
||||||
|
color: ${isCurrentUser ? Style.current.black : Style.current.white}
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
blockquote {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
${Emoji.parse(msg, "26x26")}
|
||||||
|
</body>
|
||||||
|
</html>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
horizontalAlignment: Text.AlignLeft
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
font.pixelSize: 15
|
||||||
|
readOnly: true
|
||||||
|
selectByMouse: true
|
||||||
|
color: Style.current.textColor
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
import QtQuick 2.3
|
||||||
|
import "../../../../../shared"
|
||||||
|
import "../../../../../imports"
|
||||||
|
|
||||||
|
StyledTextEdit {
|
||||||
|
id: chatTime
|
||||||
|
visible: (isEmoji || isMessage || isSticker)
|
||||||
|
color: Style.current.darkGrey
|
||||||
|
text: {
|
||||||
|
let messageDate = new Date(Math.floor(timestamp))
|
||||||
|
let minutes = messageDate.getMinutes();
|
||||||
|
let hours = messageDate.getHours();
|
||||||
|
return (hours < 10 ? "0" + hours : hours) + ":" + (minutes < 10 ? "0" + minutes : minutes)
|
||||||
|
}
|
||||||
|
font.pixelSize: 10
|
||||||
|
readOnly: true
|
||||||
|
selectByMouse: true
|
||||||
|
}
|
|
@ -0,0 +1,98 @@
|
||||||
|
import QtQuick 2.3
|
||||||
|
import "../../../../../shared"
|
||||||
|
import "../../../../../imports"
|
||||||
|
|
||||||
|
Item {
|
||||||
|
property var clickMessage: function () {}
|
||||||
|
property int chatHorizontalPadding: 12
|
||||||
|
property int chatVerticalPadding: 7
|
||||||
|
|
||||||
|
id: chatTextItem
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.topMargin: authorCurrentMsg != authorPrevMsg ? Style.current.smallPadding : 0
|
||||||
|
height: childrenRect.height + this.anchors.topMargin
|
||||||
|
width: parent.width
|
||||||
|
|
||||||
|
|
||||||
|
// FIXME @jonathanr: Adding this breaks the first line. Need to fix the height somehow
|
||||||
|
// DateGroup {
|
||||||
|
// id: dateGroupLbl
|
||||||
|
// }
|
||||||
|
|
||||||
|
UserImage {
|
||||||
|
id: chatImage
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: Style.current.padding
|
||||||
|
// anchors.top: dateGroupLbl.visible ? dateGroupLbl.bottom : parent.top
|
||||||
|
anchors.top: parent.top
|
||||||
|
}
|
||||||
|
|
||||||
|
UsernameLabel {
|
||||||
|
id: chatName
|
||||||
|
anchors.leftMargin: chatTextItem.chatHorizontalPadding
|
||||||
|
// anchors.top: dateGroupLbl.visible ? dateGroupLbl.bottom : parent.top
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: chatImage.right
|
||||||
|
}
|
||||||
|
|
||||||
|
ChatReply {
|
||||||
|
id: chatReply
|
||||||
|
// anchors.top: chatName.visible ? chatName.bottom : (dateGroupLbl.visible ? dateGroupLbl.bottom : parent.top)
|
||||||
|
anchors.top: chatName.visible ? chatName.bottom : parent.top
|
||||||
|
anchors.topMargin: chatName.visible && this.visible ? chatTextItem.chatVerticalPadding : 0
|
||||||
|
anchors.left: chatImage.right
|
||||||
|
anchors.leftMargin: chatTextItem.chatHorizontalPadding
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: chatTextItem.chatHorizontalPadding
|
||||||
|
}
|
||||||
|
|
||||||
|
ChatText {
|
||||||
|
id: chatText
|
||||||
|
anchors.top: chatReply.bottom
|
||||||
|
anchors.topMargin: chatName.visible && this.visible ? chatTextItem.chatVerticalPadding : 0
|
||||||
|
anchors.left: chatImage.right
|
||||||
|
anchors.leftMargin: chatTextItem.chatHorizontalPadding
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: chatTextItem.chatHorizontalPadding
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: stickerContainer
|
||||||
|
visible: contentType === Constants.stickerType
|
||||||
|
color: Style.current.transparent
|
||||||
|
border.color: Style.current.grey
|
||||||
|
border.width: 1
|
||||||
|
radius: 16
|
||||||
|
width: stickerId.width
|
||||||
|
height: stickerId.height
|
||||||
|
anchors.left: chatText.left
|
||||||
|
anchors.top: chatName.visible ? chatName.bottom : parent.top
|
||||||
|
anchors.topMargin: this.visible && chatName.visible ? chatTextItem.chatVerticalPadding : 0
|
||||||
|
|
||||||
|
Sticker {
|
||||||
|
id: stickerId
|
||||||
|
visible: stickerContainer.visible
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageMouseArea {
|
||||||
|
anchors.fill: stickerContainer.visible ? stickerContainer : chatText
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO show date for not the first messsage (on hover maybe)
|
||||||
|
ChatTime {
|
||||||
|
id: chatTime
|
||||||
|
visible: authorCurrentMsg != authorPrevMsg
|
||||||
|
anchors.verticalCenter: chatName.verticalCenter
|
||||||
|
anchors.left: chatName.right
|
||||||
|
anchors.leftMargin: Style.current.padding
|
||||||
|
}
|
||||||
|
|
||||||
|
SentMessage {
|
||||||
|
id: sentMessage
|
||||||
|
visible: isCurrentUser && outgoingStatus != "sent"
|
||||||
|
anchors.verticalCenter: chatTime.verticalCenter
|
||||||
|
anchors.left: chatTime.right
|
||||||
|
anchors.rightMargin: 5
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
import QtQuick 2.3
|
||||||
|
import "../../../../../shared"
|
||||||
|
import "../../../../../imports"
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
id: dateGroupLbl
|
||||||
|
font.pixelSize: 13
|
||||||
|
color: Style.current.darkGrey
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
text: {
|
||||||
|
if (prevMessageIndex == -1) return ""; // identifier
|
||||||
|
|
||||||
|
let now = new Date()
|
||||||
|
let yesterday = new Date()
|
||||||
|
yesterday.setDate(now.getDate()-1)
|
||||||
|
|
||||||
|
let prevMsgTimestamp = chatsModel.messageList.getMessageData(prevMessageIndex, "timestamp")
|
||||||
|
var currentMsgDate = new Date(parseInt(timestamp, 10));
|
||||||
|
var prevMsgDate = prevMsgTimestamp === "" ? new Date(0) : new Date(parseInt(prevMsgTimestamp, 10));
|
||||||
|
if(currentMsgDate.getDay() !== prevMsgDate.getDay()){
|
||||||
|
if (now.toDateString() === currentMsgDate.toDateString()) {
|
||||||
|
return qsTr("Today")
|
||||||
|
} else if (yesterday.toDateString() === currentMsgDate.toDateString()) {
|
||||||
|
//% "Yesterday"
|
||||||
|
return qsTrId("yesterday")
|
||||||
|
} else {
|
||||||
|
const monthNames = [
|
||||||
|
qsTr("January"),
|
||||||
|
qsTr("February"),
|
||||||
|
qsTr("March"),
|
||||||
|
qsTr("April"),
|
||||||
|
qsTr("May"),
|
||||||
|
qsTr("June"),
|
||||||
|
qsTr("July"),
|
||||||
|
qsTr("August"),
|
||||||
|
qsTr("September"),
|
||||||
|
qsTr("October"),
|
||||||
|
qsTr("November"),
|
||||||
|
qsTr("December")
|
||||||
|
];
|
||||||
|
return monthNames[currentMsgDate.getMonth()] + ", " + currentMsgDate.getDay()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
visible: text !== ""
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.topMargin: this.visible ? 20 : 0
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
import QtQuick 2.3
|
||||||
|
import "../../../../../shared"
|
||||||
|
import "../../../../../imports"
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
property int chatVerticalPadding: 12
|
||||||
|
property int chatHorizontalPadding: 12
|
||||||
|
property int imageWidth: 350
|
||||||
|
|
||||||
|
id: imageChatBox
|
||||||
|
height: {
|
||||||
|
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
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
id: imageRepeater
|
||||||
|
model: 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) {
|
||||||
|
messageItem.scrollToBottom(true, messageItem)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
import QtQuick 2.3
|
||||||
|
import "../../../../../shared"
|
||||||
|
import "../../../../../imports"
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
cursorShape: chatText.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||||
|
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||||
|
onClicked: {
|
||||||
|
if(mouse.button & Qt.RightButton) {
|
||||||
|
clickMessage()
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let link = chatText.hoveredLink;
|
||||||
|
if(link.startsWith("#")){
|
||||||
|
chatsModel.joinChat(link.substring(1), Constants.chatTypePublic);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (link.startsWith('//')) {
|
||||||
|
let pk = link.replace("//", "");
|
||||||
|
profileClick(chatsModel.userNameOrAlias(pk), pk, chatsModel.generateIdenticon(pk))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Qt.openUrlExternally(link)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,152 @@
|
||||||
|
import QtQuick 2.3
|
||||||
|
import "../../../../../shared"
|
||||||
|
import "../../../../../imports"
|
||||||
|
|
||||||
|
Item {
|
||||||
|
property var clickMessage: function () {}
|
||||||
|
property bool showImages: messageItem.appSettings.displayChatImages && imageUrls != ""
|
||||||
|
|
||||||
|
id: chatTextItem
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.topMargin: authorCurrentMsg != authorPrevMsg ? Style.current.smallPadding : 0
|
||||||
|
height: childrenRect.height + this.anchors.topMargin
|
||||||
|
width: parent.width
|
||||||
|
|
||||||
|
DateGroup {
|
||||||
|
id: dateGroupLbl
|
||||||
|
}
|
||||||
|
|
||||||
|
UserImage {
|
||||||
|
id: chatImage
|
||||||
|
visible: (isMessage || isEmoji) && authorCurrentMsg != authorPrevMsg && !isCurrentUser
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: Style.current.padding
|
||||||
|
anchors.top: dateGroupLbl.visible ? dateGroupLbl.bottom : parent.top
|
||||||
|
anchors.topMargin: 20
|
||||||
|
}
|
||||||
|
|
||||||
|
UsernameLabel {
|
||||||
|
id: chatName
|
||||||
|
visible: (isMessage || isEmoji) && authorCurrentMsg != authorPrevMsg && !isCurrentUser
|
||||||
|
text: userName
|
||||||
|
anchors.leftMargin: 20
|
||||||
|
anchors.top: dateGroupLbl.visible ? dateGroupLbl.bottom : parent.top
|
||||||
|
anchors.topMargin: 0
|
||||||
|
anchors.left: chatImage.right
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
property int chatVerticalPadding: 7
|
||||||
|
property int chatHorizontalPadding: 12
|
||||||
|
|
||||||
|
id: chatBox
|
||||||
|
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))
|
||||||
|
width: {
|
||||||
|
switch(contentType){
|
||||||
|
case Constants.stickerType:
|
||||||
|
return stickerId.width + (2 * chatBox.chatHorizontalPadding);
|
||||||
|
default:
|
||||||
|
return plainText.length > 54 ? 400 : chatText.width + 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: authorCurrentMsg != authorPrevMsg && !isCurrentUser ? chatImage.top : (dateGroupLbl.visible ? dateGroupLbl.bottom : parent.top)
|
||||||
|
anchors.topMargin: 0
|
||||||
|
visible: isMessage || isEmoji
|
||||||
|
|
||||||
|
ChatReply {
|
||||||
|
id: chatReply
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.topMargin: chatReply.visible ? chatBox.chatVerticalPadding : 0
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: Style.current.padding
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: chatBox.chatHorizontalPadding
|
||||||
|
color: isCurrentUser ? Style.current.blue : Style.current.lightBlue
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ChatText {
|
||||||
|
id: chatText
|
||||||
|
anchors.top: chatReply.bottom
|
||||||
|
anchors.topMargin: chatBox.chatVerticalPadding
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: parent.chatHorizontalPadding
|
||||||
|
anchors.right: plainText.length > 52 ? parent.right : undefined
|
||||||
|
anchors.rightMargin: plainText.length > 52 ? parent.chatHorizontalPadding : 0
|
||||||
|
horizontalAlignment: !isCurrentUser ? Text.AlignLeft : Text.AlignRight
|
||||||
|
color: !isCurrentUser ? Style.current.textColor : Style.current.currentUserTextColor
|
||||||
|
}
|
||||||
|
|
||||||
|
Sticker {
|
||||||
|
id: stickerId
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: parent.chatHorizontalPadding
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.topMargin: chatBox.chatVerticalPadding
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageMouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ChatTime {
|
||||||
|
id: chatTime
|
||||||
|
anchors.top: showImages ? imageLoader.bottom : chatBox.bottom
|
||||||
|
anchors.topMargin: 4
|
||||||
|
anchors.bottomMargin: Style.current.padding
|
||||||
|
anchors.right: showImages ? imageLoader.right : chatBox.right
|
||||||
|
anchors.rightMargin: isCurrentUser ? 5 : Style.current.padding
|
||||||
|
}
|
||||||
|
|
||||||
|
SentMessage {
|
||||||
|
id: sentMessage
|
||||||
|
anchors.top: chatTime.top
|
||||||
|
anchors.bottomMargin: Style.current.padding
|
||||||
|
anchors.right: chatTime.left
|
||||||
|
anchors.rightMargin: 5
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: imageLoader
|
||||||
|
active: showImages
|
||||||
|
sourceComponent: imageComponent
|
||||||
|
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: messageItem.appSettings.displayChatImages && imageUrls != "" ? chatBox.bottom : chatTime.bottom
|
||||||
|
anchors.topMargin: Style.current.smallPadding
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: imageComponent
|
||||||
|
ImageMessage {}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
import QtQuick 2.3
|
||||||
|
import "../../../../../shared"
|
||||||
|
import "../../../../../imports"
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
id: sentMessage
|
||||||
|
visible: isCurrentUser && (isEmoji || isMessage || isSticker)
|
||||||
|
color: Style.current.darkGrey
|
||||||
|
text: outgoingStatus == "sent" ?
|
||||||
|
//% "Sent"
|
||||||
|
qsTrId("status-sent") :
|
||||||
|
//% "Sending..."
|
||||||
|
qsTrId("sending")
|
||||||
|
font.pixelSize: 10
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
import QtQuick 2.3
|
||||||
|
import "../../../../../imports"
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: stickerId
|
||||||
|
visible: contentType === Constants.stickerType
|
||||||
|
width: 140
|
||||||
|
height: this.visible ? 140 : 0
|
||||||
|
source: this.visible ? ("https://ipfs.infura.io/ipfs/" + sticker) : ""
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
import QtQuick 2.3
|
||||||
|
import "../../../../../shared"
|
||||||
|
import "../../../../../imports"
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: chatImage
|
||||||
|
visible: (isMessage || isEmoji) && authorCurrentMsg != authorPrevMsg
|
||||||
|
width: identiconImage.width
|
||||||
|
height: identiconImage.height
|
||||||
|
border.width: 1
|
||||||
|
border.color: Style.current.border
|
||||||
|
radius: 50
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: identiconImage
|
||||||
|
width: 36
|
||||||
|
height: chatImage.visible ? 36 : 0
|
||||||
|
fillMode: Image.PreserveAspectFit
|
||||||
|
source: !isCurrentUser ? identicon : profileModel.profile.identicon
|
||||||
|
mipmap: true
|
||||||
|
smooth: false
|
||||||
|
antialiasing: true
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
anchors.fill: parent
|
||||||
|
onClicked: {
|
||||||
|
clickMessage()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
import QtQuick 2.3
|
||||||
|
import "../../../../../shared"
|
||||||
|
import "../../../../../imports"
|
||||||
|
|
||||||
|
StyledTextEdit {
|
||||||
|
id: chatName
|
||||||
|
visible: (isMessage || isEmoji) && authorCurrentMsg != authorPrevMsg
|
||||||
|
height: this.visible ? 18 : 0
|
||||||
|
text: !isCurrentUser ? userName : qsTr("You")
|
||||||
|
font.bold: true
|
||||||
|
font.pixelSize: 14
|
||||||
|
readOnly: true
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
selectByMouse: true
|
||||||
|
MouseArea {
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
anchors.fill: parent
|
||||||
|
onClicked: {
|
||||||
|
clickMessage()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
ChannelIdentifier 1.0 ChannelIdentifier.qml
|
|
@ -4,3 +4,5 @@ ChatInput 1.0 ChatInput.qml
|
||||||
EmptyChat 1.0 EmptyChat.qml
|
EmptyChat 1.0 EmptyChat.qml
|
||||||
ChatButtons 1.0 ChatButtons.qml
|
ChatButtons 1.0 ChatButtons.qml
|
||||||
ReplyArea 1.0 ReplyArea.qml
|
ReplyArea 1.0 ReplyArea.qml
|
||||||
|
Message 1.0 Message.qml
|
||||||
|
CompactMessage 1.0 CompactMessage.qml
|
||||||
|
|
|
@ -12,19 +12,6 @@ Item {
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
function linkify(inputText) {
|
|
||||||
//URLs starting with http://, https://, or ftp://
|
|
||||||
var replacePattern1 = /(\b(https?|ftp):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gim;
|
|
||||||
var replacedText = inputText.replace(replacePattern1, "<a href='$1'>$1</a>");
|
|
||||||
|
|
||||||
//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<a href='http://$2'>$2</a>");
|
|
||||||
|
|
||||||
replacedText = XSS.filterXSS(replacedText)
|
|
||||||
return replacedText;
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
id: element8
|
id: element8
|
||||||
//% "Help menus: FAQ, Glossary, etc."
|
//% "Help menus: FAQ, Glossary, etc."
|
||||||
|
@ -39,7 +26,7 @@ Item {
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
text: linkify(link)
|
text: Utils.linkifyAndXSS(link)
|
||||||
onLinkActivated: Qt.openUrlExternally(link)
|
onLinkActivated: Qt.openUrlExternally(link)
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
pragma Singleton
|
pragma Singleton
|
||||||
|
|
||||||
import QtQuick 2.13
|
import QtQuick 2.13
|
||||||
|
import "../shared/xss.js" as XSS
|
||||||
|
|
||||||
QtObject {
|
QtObject {
|
||||||
function isHex(value) {
|
function isHex(value) {
|
||||||
|
@ -39,4 +40,17 @@ QtObject {
|
||||||
}
|
}
|
||||||
return addr.substring(0, 2 + numberOfChars) + "..." + addr.substring(addr.length - numberOfChars);
|
return addr.substring(0, 2 + numberOfChars) + "..." + addr.substring(addr.length - numberOfChars);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function linkifyAndXSS(inputText) {
|
||||||
|
//URLs starting with http://, https://, or ftp://
|
||||||
|
var replacePattern1 = /(\b(https?|ftp):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gim;
|
||||||
|
var replacedText = inputText.replace(replacePattern1, "<a href='$1'>$1</a>");
|
||||||
|
|
||||||
|
//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<a href='http://$2'>$2</a>");
|
||||||
|
|
||||||
|
replacedText = XSS.filterXSS(replacedText)
|
||||||
|
return replacedText;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,6 +55,22 @@ else: unix:!android: target.path = /opt/$${TARGET}/bin
|
||||||
!isEmpty(target.path): INSTALLS += target
|
!isEmpty(target.path): INSTALLS += target
|
||||||
|
|
||||||
DISTFILES += \
|
DISTFILES += \
|
||||||
|
app/AppLayouts/Chat/ChatColumn/CompactMessage.qml \
|
||||||
|
app/AppLayouts/Chat/ChatColumn/MessageComponents/ChannelIdentifier.qml \
|
||||||
|
app/AppLayouts/Chat/ChatColumn/MessageComponents/ChatReply \
|
||||||
|
app/AppLayouts/Chat/ChatColumn/MessageComponents/ChatReply.qml \
|
||||||
|
app/AppLayouts/Chat/ChatColumn/MessageComponents/ChatText.qml \
|
||||||
|
app/AppLayouts/Chat/ChatColumn/MessageComponents/ChatTime.qml \
|
||||||
|
app/AppLayouts/Chat/ChatColumn/MessageComponents/CompactMessage.qml \
|
||||||
|
app/AppLayouts/Chat/ChatColumn/MessageComponents/DateGroup.qml \
|
||||||
|
app/AppLayouts/Chat/ChatColumn/MessageComponents/ImageMessage.qml \
|
||||||
|
app/AppLayouts/Chat/ChatColumn/MessageComponents/MessageMouseArea.qml \
|
||||||
|
app/AppLayouts/Chat/ChatColumn/MessageComponents/NormalMessage.qml \
|
||||||
|
app/AppLayouts/Chat/ChatColumn/MessageComponents/SentMessage.qml \
|
||||||
|
app/AppLayouts/Chat/ChatColumn/MessageComponents/Sticker.qml \
|
||||||
|
app/AppLayouts/Chat/ChatColumn/MessageComponents/UserImage.qml \
|
||||||
|
app/AppLayouts/Chat/ChatColumn/MessageComponents/UsernameLabel.qml \
|
||||||
|
app/AppLayouts/Chat/ChatColumn/MessageComponents/qmldir \
|
||||||
app/AppLayouts/Chat/ContactsColumn/ClosedEmptyView.qml \
|
app/AppLayouts/Chat/ContactsColumn/ClosedEmptyView.qml \
|
||||||
app/AppLayouts/Chat/components/EmojiPopup.qml \
|
app/AppLayouts/Chat/components/EmojiPopup.qml \
|
||||||
app/AppLayouts/Chat/components/InviteFriendsPopup.qml \
|
app/AppLayouts/Chat/components/InviteFriendsPopup.qml \
|
||||||
|
|
Loading…
Reference in New Issue