fix(StatusMessage): Design update and minor improvements (#752)
This commit is contained in:
parent
a9c0c88101
commit
6c107b5760
|
@ -14,9 +14,10 @@ ListView {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: 15
|
anchors.margins: 15
|
||||||
clip: true
|
clip: true
|
||||||
|
|
||||||
delegate: StatusMessage {
|
delegate: StatusMessage {
|
||||||
id: delegate
|
id: delegate
|
||||||
width: parent.width
|
width: ListView.view.width
|
||||||
|
|
||||||
audioMessageInfoText: "Audio Message"
|
audioMessageInfoText: "Audio Message"
|
||||||
cancelButtonText: "Cancel"
|
cancelButtonText: "Cancel"
|
||||||
|
@ -26,44 +27,70 @@ ListView {
|
||||||
resendText: "Resend"
|
resendText: "Resend"
|
||||||
pinnedMsgInfoText: "Pinned by"
|
pinnedMsgInfoText: "Pinned by"
|
||||||
|
|
||||||
|
timestamp: model.timestamp
|
||||||
|
isAReply: model.isReply
|
||||||
|
hasMention: model.hasMention
|
||||||
|
isPinned: model.isPinned
|
||||||
|
pinnedBy: model.pinnedBy
|
||||||
|
hasExpired: model.hasExpired
|
||||||
|
reactionsModel: model.reactions || []
|
||||||
|
|
||||||
messageDetails: StatusMessageDetails {
|
messageDetails: StatusMessageDetails {
|
||||||
contentType: model.contentType
|
contentType: model.contentType
|
||||||
messageContent: model.messageContent
|
messageContent: model.messageContent
|
||||||
amISender: model.amIsender
|
amISender: model.amIsender
|
||||||
displayName: model.userName
|
sender.id: model.senderId
|
||||||
secondaryName: model.localName !== "" && model.ensName.startsWith("@") ? model.ensName: ""
|
sender.userName: model.userName
|
||||||
chatID: model.chatKey
|
sender.localName: model.localName
|
||||||
profileImage: StatusImageSettings {
|
sender.ensName: model.ensName
|
||||||
|
sender.isContact: model.isContact
|
||||||
|
sender.trustIndicator: model.trustIndicator
|
||||||
|
sender.profileImage {
|
||||||
width: 40
|
width: 40
|
||||||
height: 40
|
height: 40
|
||||||
source: model.profileImage
|
pubkey: model.senderId
|
||||||
isIdenticon: model.isIdenticon
|
source: model.profileImage || ""
|
||||||
|
colorId: 1
|
||||||
|
colorHash: ListModel {
|
||||||
|
ListElement { colorId: 13; segmentLength: 5 }
|
||||||
|
ListElement { colorId: 31; segmentLength: 5 }
|
||||||
|
ListElement { colorId: 10; segmentLength: 1 }
|
||||||
|
ListElement { colorId: 2; segmentLength: 5 }
|
||||||
|
ListElement { colorId: 26; segmentLength: 2 }
|
||||||
|
ListElement { colorId: 19; segmentLength: 4 }
|
||||||
|
ListElement { colorId: 28; segmentLength: 3 }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
messageText: model.message
|
messageText: model.message
|
||||||
hasMention: model.hasMention
|
|
||||||
isContact: model.isContact
|
|
||||||
trustIndicator: model.trustIndicator
|
|
||||||
isPinned: model.isPinned
|
|
||||||
pinnedBy: model.pinnedBy
|
|
||||||
hasExpired: model.hasExpired
|
|
||||||
}
|
}
|
||||||
timestamp.text: "10:00 am"
|
|
||||||
timestamp.tooltip.text: "10:01 am"
|
|
||||||
// reply related data
|
|
||||||
isAReply: model.isReply
|
|
||||||
replyDetails: StatusMessageDetails {
|
replyDetails: StatusMessageDetails {
|
||||||
amISender: model.isReply ? model.replyAmISender : ""
|
amISender: model.isReply && model.replyAmISender
|
||||||
displayName: model.isReply ? model.replySenderName: ""
|
sender.id: model.replySenderId || ""
|
||||||
profileImage: StatusImageSettings {
|
sender.userName: model.isReply ? model.replySenderName: ""
|
||||||
|
sender.ensName: model.isReply ? model.replySenderEnsName : ""
|
||||||
|
sender.profileImage {
|
||||||
width: 20
|
width: 20
|
||||||
height: 20
|
height: 20
|
||||||
|
pubkey: model.replySenderId
|
||||||
source: model.isReply ? model.replyProfileImage: ""
|
source: model.isReply ? model.replyProfileImage: ""
|
||||||
isIdenticon: model.isReply ? model.replyIsIdenticon: ""
|
colorId: 1
|
||||||
|
colorHash: ListModel {
|
||||||
|
ListElement { colorId: 13; segmentLength: 5 }
|
||||||
|
ListElement { colorId: 31; segmentLength: 5 }
|
||||||
|
ListElement { colorId: 10; segmentLength: 1 }
|
||||||
|
ListElement { colorId: 2; segmentLength: 5 }
|
||||||
|
ListElement { colorId: 26; segmentLength: 2 }
|
||||||
|
ListElement { colorId: 19; segmentLength: 4 }
|
||||||
|
ListElement { colorId: 28; segmentLength: 3 }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
messageText: model.isReply ? model.replyMessageText: ""
|
messageText: model.isReply ? model.replyMessageText: ""
|
||||||
contentType: model.replyContentType
|
contentType: model.replyContentType
|
||||||
messageContent: model.replyMessageContent
|
messageContent: model.replyMessageContent
|
||||||
}
|
}
|
||||||
|
|
||||||
quickActions: [
|
quickActions: [
|
||||||
StatusFlatRoundButton {
|
StatusFlatRoundButton {
|
||||||
id: emojiBtn
|
id: emojiBtn
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,54 @@
|
||||||
|
import QtQuick 2.14
|
||||||
|
|
||||||
|
import StatusQ.Core 0.1
|
||||||
|
import StatusQ.Core.Theme 0.1
|
||||||
|
|
||||||
|
StatusBaseText {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property int previousMessageIndex: -1
|
||||||
|
property double previousMessageTimestamp
|
||||||
|
property double messageTimestamp
|
||||||
|
|
||||||
|
font.pixelSize: 13
|
||||||
|
color: Theme.palette.baseColor1
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
|
||||||
|
text: {
|
||||||
|
if (previousMessageIndex === -1)
|
||||||
|
return "";
|
||||||
|
|
||||||
|
const now = new Date()
|
||||||
|
const yesterday = new Date()
|
||||||
|
yesterday.setDate(now.getDate()-1)
|
||||||
|
|
||||||
|
const currentMsgDate = new Date(messageTimestamp);
|
||||||
|
const prevMsgDate = new Date(previousMessageTimestamp);
|
||||||
|
|
||||||
|
if (!!prevMsgDate && currentMsgDate.getDay() === prevMsgDate.getDay())
|
||||||
|
return "";
|
||||||
|
|
||||||
|
if (now == currentMsgDate)
|
||||||
|
return qsTr("Today");
|
||||||
|
|
||||||
|
if (yesterday == currentMsgDate)
|
||||||
|
return qsTr("Yesterday");
|
||||||
|
|
||||||
|
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.getDate();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,14 +1,16 @@
|
||||||
import QtQuick 2.14
|
import QtQuick 2.14
|
||||||
import QtQuick.Layouts 1.14
|
import QtQuick.Layouts 1.14
|
||||||
|
import QtQuick.Controls 2.14
|
||||||
|
|
||||||
import StatusQ.Core 0.1
|
import StatusQ.Core 0.1
|
||||||
import StatusQ.Core.Theme 0.1
|
import StatusQ.Core.Theme 0.1
|
||||||
|
import StatusQ.Core.Utils 0.1
|
||||||
import StatusQ.Controls 0.1
|
import StatusQ.Controls 0.1
|
||||||
|
|
||||||
import "./private/statusMessage"
|
import "./private/statusMessage"
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: statusMessage
|
id: root
|
||||||
|
|
||||||
enum ContentType {
|
enum ContentType {
|
||||||
Unknown = 0,
|
Unknown = 0,
|
||||||
|
@ -21,12 +23,12 @@ Rectangle {
|
||||||
Invitation = 7
|
Invitation = 7
|
||||||
}
|
}
|
||||||
|
|
||||||
property alias messageHeader: messageHeader
|
property alias quickActions: quickActionsPanel.items
|
||||||
property alias quickActions:quickActionsPanel.quickActions
|
|
||||||
property alias statusChatInput: editComponent.inputComponent
|
property alias statusChatInput: editComponent.inputComponent
|
||||||
property alias linksComponent: linksLoader.sourceComponent
|
property alias linksComponent: linksLoader.sourceComponent
|
||||||
property alias footerComponent: footer.sourceComponent
|
property alias transcationComponent: transactionBubbleLoader.sourceComponent
|
||||||
property alias timestamp: messageHeader.timestamp
|
property alias invitationComponent: invitationBubbleLoader.sourceComponent
|
||||||
|
property alias mouseArea: mouseArea
|
||||||
|
|
||||||
property string resendText: ""
|
property string resendText: ""
|
||||||
property string cancelButtonText: ""
|
property string cancelButtonText: ""
|
||||||
|
@ -35,155 +37,360 @@ Rectangle {
|
||||||
property string errorLoadingImageText: ""
|
property string errorLoadingImageText: ""
|
||||||
property string audioMessageInfoText: ""
|
property string audioMessageInfoText: ""
|
||||||
property string pinnedMsgInfoText: ""
|
property string pinnedMsgInfoText: ""
|
||||||
|
property var reactionIcons: [
|
||||||
|
Emoji.iconSource("❤"),
|
||||||
|
Emoji.iconSource("👍"),
|
||||||
|
Emoji.iconSource("👎"),
|
||||||
|
Emoji.iconSource("🤣"),
|
||||||
|
Emoji.iconSource("😥"),
|
||||||
|
Emoji.iconSource("😠")
|
||||||
|
]
|
||||||
|
|
||||||
|
property string messageId: ""
|
||||||
property bool isAppWindowActive: false
|
property bool isAppWindowActive: false
|
||||||
property bool editMode: false
|
property bool editMode: false
|
||||||
property bool isAReply: false
|
property bool isAReply: false
|
||||||
|
property bool isEdited: false
|
||||||
|
property bool isChatBlocked: false
|
||||||
|
|
||||||
|
property bool hasMention: false
|
||||||
|
property bool isPinned: false
|
||||||
|
property string pinnedBy: ""
|
||||||
|
property bool hasExpired: false
|
||||||
|
property double timestamp: 0
|
||||||
|
property var reactionsModel: []
|
||||||
|
|
||||||
|
readonly property bool dateGroupVisible: dateGroupLabel.visible
|
||||||
|
property bool showHeader: true
|
||||||
|
property bool isActiveMessage: false
|
||||||
|
property bool disableHover: false
|
||||||
|
property bool hideQuickActions: false
|
||||||
|
property color overrideBackgroundColor: "transparent"
|
||||||
|
property bool overrideBackground: false
|
||||||
|
|
||||||
|
property alias previousMessageIndex: dateGroupLabel.previousMessageIndex
|
||||||
|
property alias previousMessageTimestamp: dateGroupLabel.previousMessageTimestamp
|
||||||
|
|
||||||
property StatusMessageDetails messageDetails: StatusMessageDetails {}
|
property StatusMessageDetails messageDetails: StatusMessageDetails {}
|
||||||
property StatusMessageDetails replyDetails: StatusMessageDetails {}
|
property StatusMessageDetails replyDetails: StatusMessageDetails {}
|
||||||
|
|
||||||
signal profilePictureClicked()
|
property string timestampString: Qt.formatTime(new Date(timestamp), "hh:mm");
|
||||||
signal senderNameClicked()
|
property string timestampTooltipString: Qt.formatTime(new Date(timestamp), "dddd, MMMM d, yyyy hh:mm:ss t");
|
||||||
signal editCompleted(var newMsgText)
|
|
||||||
signal replyProfileClicked()
|
signal clicked(var sender, var mouse)
|
||||||
signal stickerLoaded()
|
signal profilePictureClicked(var sender, var mouse)
|
||||||
signal imageClicked(var imageSource)
|
signal senderNameClicked(var sender, var mouse)
|
||||||
|
signal replyProfileClicked(var sender, var mouse)
|
||||||
|
|
||||||
|
signal addReactionClicked(var sender, var mouse)
|
||||||
|
signal toggleReactionClicked(int emojiId)
|
||||||
|
signal imageClicked(var image, var mouse, var imageSource)
|
||||||
|
signal stickerClicked()
|
||||||
signal resendClicked()
|
signal resendClicked()
|
||||||
|
|
||||||
height: childrenRect.height
|
signal editCompleted(var newMsgText)
|
||||||
color: hoverHandler.hovered ? (messageDetails.hasMention ? Theme.palette.mentionColor3 : messageDetails.isPinned ? Theme.palette.pinColor2 : Theme.palette.baseColor2) : messageDetails.hasMention ? Theme.palette.mentionColor4 : messageDetails.isPinned ? Theme.palette.pinColor3 : "transparent"
|
signal editCancelled()
|
||||||
|
signal stickerLoaded()
|
||||||
|
signal linkActivated(string link)
|
||||||
|
|
||||||
|
signal hoverChanged(string messageId, bool hovered)
|
||||||
|
signal activeChanged(string messageId, bool active)
|
||||||
|
|
||||||
|
function startMessageFoundAnimation() {
|
||||||
|
messageFoundAnimation.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
implicitWidth: messageLayout.implicitWidth
|
||||||
|
+ messageLayout.anchors.leftMargin
|
||||||
|
+ messageLayout.anchors.rightMargin
|
||||||
|
|
||||||
|
implicitHeight: messageLayout.implicitHeight
|
||||||
|
+ messageLayout.anchors.topMargin
|
||||||
|
+ messageLayout.anchors.bottomMargin
|
||||||
|
|
||||||
|
color: {
|
||||||
|
if (root.overrideBackground)
|
||||||
|
return root.overrideBackgroundColor;
|
||||||
|
|
||||||
|
if (root.editMode)
|
||||||
|
return Theme.palette.baseColor2;
|
||||||
|
|
||||||
|
if (hoverHandler.hovered || root.isActiveMessage) {
|
||||||
|
if (root.hasMention)
|
||||||
|
return Theme.palette.mentionColor3;
|
||||||
|
if (root.isPinned)
|
||||||
|
return Theme.palette.pinColor2;
|
||||||
|
return Theme.palette.baseColor2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (root.hasMention)
|
||||||
|
return Theme.palette.mentionColor4;
|
||||||
|
if (root.isPinned)
|
||||||
|
return Theme.palette.pinColor3;
|
||||||
|
return "transparent";
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors {
|
||||||
|
top: parent.top
|
||||||
|
bottom: parent.bottom
|
||||||
|
left: parent.left
|
||||||
|
}
|
||||||
|
width: 2
|
||||||
|
visible: root.isPinned
|
||||||
|
color: Theme.palette.pinColor1
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors {
|
||||||
|
top: parent.top
|
||||||
|
bottom: parent.bottom
|
||||||
|
left: parent.left
|
||||||
|
}
|
||||||
|
width: 2
|
||||||
|
visible: root.hasMention
|
||||||
|
color: Theme.palette.mentionColor1
|
||||||
|
}
|
||||||
|
|
||||||
|
SequentialAnimation {
|
||||||
|
id: messageFoundAnimation
|
||||||
|
|
||||||
|
PauseAnimation {
|
||||||
|
duration: 600
|
||||||
|
}
|
||||||
|
NumberAnimation {
|
||||||
|
target: highlightRect
|
||||||
|
property: "opacity"
|
||||||
|
to: 1.0
|
||||||
|
duration: 1500
|
||||||
|
}
|
||||||
|
PauseAnimation {
|
||||||
|
duration: 1000
|
||||||
|
}
|
||||||
|
NumberAnimation {
|
||||||
|
target: highlightRect
|
||||||
|
property: "opacity"
|
||||||
|
to: 0.0
|
||||||
|
duration: 1500
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: highlightRect
|
||||||
|
anchors.fill: parent
|
||||||
|
opacity: 0
|
||||||
|
visible: opacity > 0.001
|
||||||
|
color: Theme.palette.baseColor2
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: mouseArea
|
||||||
|
anchors.fill: parent
|
||||||
|
}
|
||||||
|
|
||||||
HoverHandler {
|
HoverHandler {
|
||||||
id: hoverHandler
|
id: hoverHandler
|
||||||
|
enabled: !root.isActiveMessage && !root.disableHover
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: messageLayout
|
id: messageLayout
|
||||||
width: parent.width
|
anchors.fill: parent
|
||||||
StatusMessageReply {
|
anchors.topMargin: 8
|
||||||
|
anchors.bottomMargin: 8
|
||||||
|
|
||||||
|
StatusDateGroupLabel {
|
||||||
|
id: dateGroupLabel
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
visible: isAReply
|
Layout.topMargin: 20
|
||||||
replyDetails: statusMessage.replyDetails
|
messageTimestamp: root.timestamp
|
||||||
onReplyProfileClicked: statusMessage.replyProfileClicked()
|
visible: text !== ""
|
||||||
audioMessageInfoText: statusMessage.audioMessageInfoText
|
|
||||||
}
|
}
|
||||||
RowLayout {
|
|
||||||
spacing: 8
|
Loader {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
StatusSmartIdenticon {
|
active: isAReply
|
||||||
id: profileImage
|
visible: active
|
||||||
|
sourceComponent: StatusMessageReply {
|
||||||
|
replyDetails: root.replyDetails
|
||||||
|
onReplyProfileClicked: root.replyProfileClicked(sender, mouse)
|
||||||
|
audioMessageInfoText: root.audioMessageInfoText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.leftMargin: 16
|
||||||
|
Layout.rightMargin: 16
|
||||||
|
spacing: 8
|
||||||
|
|
||||||
|
Item {
|
||||||
Layout.alignment: Qt.AlignTop
|
Layout.alignment: Qt.AlignTop
|
||||||
Layout.topMargin: 10
|
|
||||||
Layout.leftMargin: 16
|
implicitWidth: profileImage.effectiveSize.width
|
||||||
image: messageDetails.profileImage
|
implicitHeight: profileImage.visible ? profileImage.effectiveSize.height : 0
|
||||||
name: messageHeader.displayName
|
|
||||||
MouseArea {
|
StatusSmartIdenticon {
|
||||||
cursorShape: Qt.PointingHandCursor
|
id: profileImage
|
||||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
|
||||||
anchors.fill: parent
|
active: root.showHeader
|
||||||
onClicked: statusMessage.profilePictureClicked()
|
visible: active
|
||||||
|
|
||||||
|
name: root.messageDetails.sender.userName
|
||||||
|
image: root.messageDetails.sender.profileImage.imageSettings
|
||||||
|
icon: root.messageDetails.sender.profileImage.iconSettings
|
||||||
|
ringSettings: root.messageDetails.sender.profileImage.ringSettings
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||||
|
anchors.fill: parent
|
||||||
|
onClicked: root.profilePictureClicked(this, mouse)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Column {
|
|
||||||
|
ColumnLayout {
|
||||||
spacing: 4
|
spacing: 4
|
||||||
Layout.alignment: Qt.AlignTop
|
Layout.alignment: Qt.AlignTop
|
||||||
Layout.topMargin: 10
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
StatusPinMessageDetails {
|
|
||||||
visible: messageDetails.isPinned && !editMode
|
Loader {
|
||||||
pinnedMsgInfoText: statusMessage.pinnedMsgInfoText
|
active: root.isPinned && !editMode
|
||||||
pinnedBy: messageDetails.pinnedBy
|
visible: active
|
||||||
|
sourceComponent: StatusPinMessageDetails {
|
||||||
|
pinnedMsgInfoText: root.pinnedMsgInfoText
|
||||||
|
pinnedBy: root.pinnedBy
|
||||||
|
}
|
||||||
}
|
}
|
||||||
StatusMessageHeader {
|
StatusMessageHeader {
|
||||||
id: messageHeader
|
Layout.fillWidth: true
|
||||||
width: parent.width
|
sender: root.messageDetails.sender
|
||||||
displayName: messageDetails.displayName
|
amISender: root.messageDetails.amISender
|
||||||
secondaryName: messageDetails.secondaryName
|
resendText: root.resendText
|
||||||
tertiaryDetail: messageDetails.chatID
|
showResendButton: root.hasExpired && root.messageDetails.amISender
|
||||||
isContact: messageDetails.isContact
|
onClicked: root.senderNameClicked(sender, mouse)
|
||||||
trustIndicator: messageDetails.trustIndicator
|
onResendClicked: root.resendClicked()
|
||||||
resendText: statusMessage.resendText
|
visible: root.showHeader && !editMode
|
||||||
showResendButton: messageDetails.hasExpired && messageDetails.amISender
|
timestamp.text: root.timestampString
|
||||||
onClicked: statusMessage.senderNameClicked()
|
timestamp.tooltip.text: root.timestampTooltipString
|
||||||
onResendClicked: statusMessage.resendClicked()
|
|
||||||
visible: !editMode
|
|
||||||
}
|
}
|
||||||
Loader {
|
Loader {
|
||||||
active: !editMode && !!messageDetails.messageText
|
Layout.fillWidth: true
|
||||||
width: parent.width
|
active: !editMode && !!root.messageDetails.messageText
|
||||||
visible: active
|
visible: active
|
||||||
sourceComponent: StatusTextMessage {
|
sourceComponent: StatusTextMessage {
|
||||||
width: parent.width
|
textField.text: {
|
||||||
textField.text: messageDetails.messageText
|
if (root.messageDetails.contentType === StatusMessage.ContentType.Sticker)
|
||||||
|
return "";
|
||||||
|
|
||||||
|
const formattedMessage = Utils.linkifyAndXSS(root.messageDetails.messageText);
|
||||||
|
|
||||||
|
if (root.messageDetails.contentType === StatusMessage.ContentType.Emoji)
|
||||||
|
return Emoji.parse(formattedMessage, Emoji.size.middle, Emoji.format.png);
|
||||||
|
|
||||||
|
if (root.isEdited) {
|
||||||
|
const index = formattedMessage.endsWith("code>") ? formattedMessage.length : formattedMessage.length - 4;
|
||||||
|
const editedMessage = formattedMessage.slice(0, index)
|
||||||
|
+ ` <span class="isEdited">` + qsTr("(edited)") + `</span>`
|
||||||
|
+ formattedMessage.slice(index);
|
||||||
|
return Utils.getMessageWithStyle(Emoji.parse(editedMessage), textField.hoveredLink)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Utils.getMessageWithStyle(Emoji.parse(formattedMessage), textField.hoveredLink)
|
||||||
|
}
|
||||||
|
onLinkActivated: {
|
||||||
|
root.linkActivated(link);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Loader {
|
Loader {
|
||||||
active: messageDetails.contentType === StatusMessage.ContentType.Image && !editMode
|
active: root.messageDetails.contentType === StatusMessage.ContentType.Image && !editMode
|
||||||
visible: active
|
visible: active
|
||||||
sourceComponent: StatusImageMessage {
|
sourceComponent: StatusImageMessage {
|
||||||
source: messageDetails.contentType === StatusMessage.ContentType.Image ? messageDetails.messageContent : ""
|
source: root.messageDetails.contentType === StatusMessage.ContentType.Image ? root.messageDetails.messageContent : ""
|
||||||
onClicked: statusMessage.imageClicked()
|
onClicked: root.imageClicked(image, mouse, imageSource)
|
||||||
shapeType: messageDetails.amISender ? StatusImageMessage.ShapeType.RIGHT_ROUNDED : StatusImageMessage.ShapeType.LEFT_ROUNDED
|
shapeType: root.messageDetails.amISender ? StatusImageMessage.ShapeType.RIGHT_ROUNDED : StatusImageMessage.ShapeType.LEFT_ROUNDED
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
StatusSticker {
|
Loader {
|
||||||
visible: messageDetails.contentType === StatusMessage.ContentType.Sticker && !editMode
|
active: root.messageDetails.contentType === StatusMessage.ContentType.Sticker && !editMode
|
||||||
image.source: messageDetails.messageContent
|
visible: active
|
||||||
onLoaded: statusMessage.stickerLoaded()
|
sourceComponent: StatusSticker {
|
||||||
|
image.source: root.messageDetails.messageContent
|
||||||
|
onLoaded: root.stickerLoaded()
|
||||||
|
onClicked: {
|
||||||
|
root.stickerClicked()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Loader {
|
Loader {
|
||||||
active: messageDetails.contentType === StatusMessage.ContentType.Audio && !editMode
|
active: root.messageDetails.contentType === StatusMessage.ContentType.Audio && !editMode
|
||||||
visible: active
|
visible: active
|
||||||
sourceComponent: StatusAudioMessage {
|
sourceComponent: StatusAudioMessage {
|
||||||
audioSource: messageDetails.messageContent
|
audioSource: root.messageDetails.messageContent
|
||||||
hovered: hoverHandler.hovered
|
hovered: hoverHandler.hovered
|
||||||
audioMessageInfoText: statusMessage.audioMessageInfoText
|
audioMessageInfoText: root.audioMessageInfoText
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loader {
|
Loader {
|
||||||
id: linksLoader
|
id: linksLoader
|
||||||
active: !!linksLoader.sourceComponent
|
active: !root.editMode
|
||||||
visible: active
|
visible: active
|
||||||
}
|
}
|
||||||
Loader {
|
Loader {
|
||||||
id: transactionBubbleLoader
|
id: transactionBubbleLoader
|
||||||
active: messageDetails.contentType === StatusMessage.ContentType.Transaction && !editMode
|
active: root.messageDetails.contentType === StatusMessage.ContentType.Transaction && !editMode
|
||||||
visible: active
|
visible: active
|
||||||
}
|
}
|
||||||
Loader {
|
Loader {
|
||||||
id: invitationBubbleLoader
|
id: invitationBubbleLoader
|
||||||
active: messageDetails.contentType === StatusMessage.ContentType.Invitation && !editMode
|
active: root.messageDetails.contentType === StatusMessage.ContentType.Invitation && !editMode
|
||||||
visible: active
|
visible: active
|
||||||
}
|
}
|
||||||
StatusEditMessage {
|
StatusEditMessage {
|
||||||
id: editComponent
|
id: editComponent
|
||||||
width: parent.width
|
Layout.fillWidth: true
|
||||||
msgText: messageDetails.messageText
|
Layout.rightMargin: 16
|
||||||
visible: editMode
|
active: root.editMode
|
||||||
saveButtonText: statusMessage.saveButtonText
|
visible: active
|
||||||
cancelButtonText: statusMessage.cancelButtonText
|
msgText: root.messageDetails.messageText
|
||||||
onCancelEditClicked: editMode = false
|
saveButtonText: root.saveButtonText
|
||||||
onEditCompleted: {
|
cancelButtonText: root.cancelButtonText
|
||||||
editMode = false
|
onEditCancelled: root.editCancelled()
|
||||||
statusMessage.editCompleted(newMsgText)
|
onEditCompleted: root.editCompleted(newMsgText)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
StatusBaseText {
|
StatusBaseText {
|
||||||
id: retryLbl
|
|
||||||
color: Theme.palette.dangerColor1
|
color: Theme.palette.dangerColor1
|
||||||
text: statusMessage.resendText
|
text: root.resendText
|
||||||
font.pixelSize: 12
|
font.pixelSize: 12
|
||||||
visible: messageDetails.hasExpired && messageDetails.amISender && !messageDetails.timestamp && !editMode
|
visible: root.hasExpired && root.messageDetails.amISender && !root.timestamp && !editMode
|
||||||
MouseArea {
|
MouseArea {
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
onClicked: statusMessage.resendClicked()
|
onClicked: root.resendClicked()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loader {
|
Loader {
|
||||||
id: footer
|
active: root.reactionsModel.count > 0
|
||||||
active: sourceComponent && !editMode
|
|
||||||
visible: active
|
visible: active
|
||||||
|
sourceComponent: StatusMessageEmojiReactions {
|
||||||
|
id: emojiReactionsPanel
|
||||||
|
|
||||||
|
emojiReactionsModel: root.reactionsModel
|
||||||
|
store: root.messageStore
|
||||||
|
icons: root.reactionIcons
|
||||||
|
|
||||||
|
onHoverChanged: {
|
||||||
|
root.hoverChanged(messageId, hovered)
|
||||||
|
}
|
||||||
|
|
||||||
|
isCurrentUser: root.messageDetails.amISender
|
||||||
|
onAddEmojiClicked: root.addReactionClicked(sender, mouse)
|
||||||
|
onToggleReaction: root.toggleReactionClicked(emojiID)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -195,6 +402,6 @@ Rectangle {
|
||||||
anchors.rightMargin: 20
|
anchors.rightMargin: 20
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
anchors.topMargin: -8
|
anchors.topMargin: -8
|
||||||
visible: hoverHandler.hovered && !editMode
|
visible: hoverHandler.hovered && !root.hideQuickActions
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,24 +6,13 @@ QtObject {
|
||||||
id: msgDetails
|
id: msgDetails
|
||||||
|
|
||||||
property bool amISender: false
|
property bool amISender: false
|
||||||
property string displayName: ""
|
|
||||||
property string secondaryName: ""
|
property StatusMessageSenderDetails sender: StatusMessageSenderDetails { }
|
||||||
property string chatID: ""
|
|
||||||
property StatusImageSettings profileImage: StatusImageSettings {
|
|
||||||
width: 40
|
|
||||||
height: 40
|
|
||||||
}
|
|
||||||
property bool isEdited: false
|
property bool isEdited: false
|
||||||
property string messageText: ""
|
|
||||||
property int contentType: 0
|
property int contentType: 0
|
||||||
|
property string messageText: ""
|
||||||
property string messageContent: ""
|
property string messageContent: ""
|
||||||
property bool isContact: false
|
|
||||||
property var trustIndicator: StatusContactVerificationIcons.TrustedType.None
|
|
||||||
property bool hasMention: false
|
|
||||||
property bool isPinned: false
|
|
||||||
property string pinnedBy: ""
|
|
||||||
property bool hasExpired: false
|
|
||||||
property string timestamp: ""
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
import QtQuick 2.0
|
||||||
|
import StatusQ.Core 0.1
|
||||||
|
|
||||||
|
QtObject {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property string id: ""
|
||||||
|
property string userName: ""
|
||||||
|
property string ensName: ""
|
||||||
|
property string localName: ""
|
||||||
|
|
||||||
|
property bool isContact: false
|
||||||
|
property int trustIndicator: StatusContactVerificationIcons.TrustedType.None
|
||||||
|
|
||||||
|
property StatusProfileImageSettings profileImage: StatusProfileImageSettings {
|
||||||
|
pubkey: root.id
|
||||||
|
showRing: !root.ensName
|
||||||
|
width: 40
|
||||||
|
height: 40
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly property string displayName: root.localName !== ""
|
||||||
|
? root.localName
|
||||||
|
: root.ensName !== ""
|
||||||
|
? root.ensName
|
||||||
|
: root.userName
|
||||||
|
|
||||||
|
readonly property string secondaryName: root.localName === ""
|
||||||
|
? ""
|
||||||
|
: root.ensName !== ""
|
||||||
|
? root.ensName
|
||||||
|
: root.userName
|
||||||
|
}
|
|
@ -28,6 +28,10 @@ Loader {
|
||||||
distinctiveColors: Theme.palette.identiconRingColors
|
distinctiveColors: Theme.palette.identiconRingColors
|
||||||
}
|
}
|
||||||
|
|
||||||
|
readonly property size effectiveSize: !!statusSmartIdenticon.image.source.toString()
|
||||||
|
? Qt.size(statusSmartIdenticon.image.width, statusSmartIdenticon.image.width)
|
||||||
|
: Qt.size(statusSmartIdenticon.icon.width, statusSmartIdenticon.icon.height)
|
||||||
|
|
||||||
sourceComponent: statusSmartIdenticon.icon.isLetterIdenticon ? letterIdenticon :
|
sourceComponent: statusSmartIdenticon.icon.isLetterIdenticon ? letterIdenticon :
|
||||||
!!statusSmartIdenticon.image.source.toString() ? roundedImage :
|
!!statusSmartIdenticon.image.source.toString() ? roundedImage :
|
||||||
!!statusSmartIdenticon.icon.name.toString() ? roundedIcon : letterIdenticon
|
!!statusSmartIdenticon.icon.name.toString() ? roundedIcon : letterIdenticon
|
||||||
|
|
|
@ -6,45 +6,63 @@ import StatusQ.Core.Theme 0.1
|
||||||
import StatusQ.Controls 0.1
|
import StatusQ.Controls 0.1
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: editText
|
id: root
|
||||||
|
|
||||||
property alias inputComponent: chatInputLoader.sourceComponent
|
property alias inputComponent: chatInputLoader.sourceComponent
|
||||||
|
property alias active: chatInputLoader.active
|
||||||
|
|
||||||
property string cancelButtonText: ""
|
property string cancelButtonText: ""
|
||||||
property string saveButtonText: ""
|
property string saveButtonText: ""
|
||||||
property string msgText: ""
|
property string msgText: ""
|
||||||
|
|
||||||
signal cancelEditClicked()
|
signal editCancelled()
|
||||||
signal editCompleted(var newMsgText)
|
signal editCompleted(var newMsgText)
|
||||||
|
|
||||||
height: childrenRect.height
|
implicitHeight: layout.implicitHeight
|
||||||
|
implicitWidth: layout.implicitWidth
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
|
id: layout
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
spacing: 4
|
spacing: 4
|
||||||
|
|
||||||
Loader {
|
Loader {
|
||||||
id: chatInputLoader
|
id: chatInputLoader
|
||||||
// To-Do: Move to StatusChatInput once its moved to StatusQ
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
/*
|
||||||
|
NOTE: sourceComponent must have `messageText` property
|
||||||
|
TODO: Replace with StatusChatInput once its moved to StatusQ.
|
||||||
|
*/
|
||||||
|
|
||||||
sourceComponent: StatusInput {
|
sourceComponent: StatusInput {
|
||||||
width: editText.width
|
readonly property string messageText: input.text
|
||||||
placeholderText: ""
|
width: parent.width
|
||||||
|
input.placeholderText: ""
|
||||||
input.text: msgText
|
input.text: msgText
|
||||||
maximumHeight: 40
|
maximumHeight: 40
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
spacing: 4
|
spacing: 4
|
||||||
StatusFlatButton {
|
StatusFlatButton {
|
||||||
id: cancelBtn
|
id: cancelBtn
|
||||||
text: cancelButtonText
|
text: cancelButtonText
|
||||||
size: StatusBaseButton.Size.Small
|
size: StatusBaseButton.Size.Small
|
||||||
onClicked: cancelEditClicked()
|
onClicked: {
|
||||||
|
editCancelled()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
StatusButton {
|
StatusButton {
|
||||||
id: saveBtn
|
id: saveBtn
|
||||||
text: saveButtonText
|
text: saveButtonText
|
||||||
size: StatusBaseButton.Size.Small
|
size: StatusBaseButton.Size.Small
|
||||||
enabled: chatInputLoader.item.input.text.trim().length > 0
|
enabled: !!chatInputLoader.item && chatInputLoader.item.messageText.trim().length > 0
|
||||||
onClicked: editCompleted(chatInputLoader.item.input.text)
|
onClicked: {
|
||||||
|
editCompleted(!chatInputLoader.item ? "" : chatInputLoader.item.messageText)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,10 +25,10 @@ Item {
|
||||||
property string loadingImageText: ""
|
property string loadingImageText: ""
|
||||||
property string errorLoadingImageText: ""
|
property string errorLoadingImageText: ""
|
||||||
|
|
||||||
signal clicked(var image, var mouse)
|
signal clicked(var image, var mouse, var imageSource)
|
||||||
|
|
||||||
width: loadingImage.visible ? loadingImage.width : imageMessage.width
|
implicitWidth: loadingImage.visible ? loadingImage.width : imageMessage.width
|
||||||
height: loadingImage.visible ? loadingImage.height : imageMessage.paintedHeight
|
implicitHeight: loadingImage.visible ? loadingImage.height : imageMessage.paintedHeight
|
||||||
|
|
||||||
QtObject {
|
QtObject {
|
||||||
id: _internal
|
id: _internal
|
||||||
|
@ -87,7 +87,7 @@ Item {
|
||||||
_internal.pausePlaying = ! _internal.pausePlaying
|
_internal.pausePlaying = ! _internal.pausePlaying
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
imageContainer.clicked(imageMessage, mouse)
|
imageContainer.clicked(imageMessage, mouse, imageMessage.source)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,227 @@
|
||||||
|
import QtQuick 2.3
|
||||||
|
import QtQuick.Controls 2.13
|
||||||
|
import QtGraphicalEffects 1.13
|
||||||
|
|
||||||
|
import StatusQ.Core 0.1
|
||||||
|
import StatusQ.Core.Theme 0.1
|
||||||
|
import StatusQ.Core.Utils 0.1
|
||||||
|
import StatusQ.Controls 0.1
|
||||||
|
import StatusQ.Components 0.1
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
implicitHeight: 22
|
||||||
|
implicitWidth: childrenRect.width
|
||||||
|
|
||||||
|
property int imageMargin: 4
|
||||||
|
signal addEmojiClicked(var sender, var mouse)
|
||||||
|
signal hoverChanged(bool hovered)
|
||||||
|
signal toggleReaction(int emojiID)
|
||||||
|
|
||||||
|
property var store
|
||||||
|
property bool isCurrentUser
|
||||||
|
property var emojiReactionsModel
|
||||||
|
|
||||||
|
property var icons: []
|
||||||
|
|
||||||
|
QtObject {
|
||||||
|
id: d
|
||||||
|
|
||||||
|
function lastTwoItems(nodes) {
|
||||||
|
return nodes.join(qsTr(" and "));
|
||||||
|
}
|
||||||
|
|
||||||
|
function showReactionAuthors(jsonArrayOfUsersReactedWithThisEmoji, emojiId) {
|
||||||
|
const listOfUsers = JSON.parse(jsonArrayOfUsersReactedWithThisEmoji)
|
||||||
|
if (listOfUsers.error) {
|
||||||
|
console.error("error parsing users who reacted to a message, error: ", obj.error)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let author;
|
||||||
|
if (listOfUsers.length === 1) {
|
||||||
|
author = listOfUsers[0]
|
||||||
|
} else if (listOfUsers.length === 2) {
|
||||||
|
author = lastTwoItems(listOfUsers);
|
||||||
|
} else {
|
||||||
|
var leftNode = [];
|
||||||
|
var rightNode = [];
|
||||||
|
const maxReactions = 12
|
||||||
|
let maximum = Math.min(maxReactions, listOfUsers.length)
|
||||||
|
|
||||||
|
if (listOfUsers.length > maxReactions) {
|
||||||
|
leftNode = listOfUsers.slice(0, maxReactions);
|
||||||
|
rightNode = listOfUsers.slice(maxReactions, listOfUsers.length);
|
||||||
|
return (rightNode.length === 1) ?
|
||||||
|
lastTwoItems([leftNode.join(", "), rightNode[0]]) :
|
||||||
|
lastTwoItems([leftNode.join(", "), qsTr("%1 more").arg(rightNode.length)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
leftNode = listOfUsers.slice(0, maximum - 1);
|
||||||
|
rightNode = listOfUsers.slice(maximum - 1, listOfUsers.length);
|
||||||
|
author = lastTwoItems([leftNode.join(", "), rightNode[0]])
|
||||||
|
}
|
||||||
|
return qsTr("%1 reacted with %2")
|
||||||
|
.arg(author)
|
||||||
|
.arg(Emoji.getEmojiFromId(emojiId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
spacing: root.imageMargin
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
id: reactionRepeater
|
||||||
|
width: childrenRect.width
|
||||||
|
model: root.emojiReactionsModel
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: emojiContainer
|
||||||
|
|
||||||
|
readonly property bool isHovered: mouseArea.containsMouse
|
||||||
|
|
||||||
|
width: emojiImage.width + emojiCount.width + (root.imageMargin * 2) + + 8
|
||||||
|
height: 20
|
||||||
|
radius: 10
|
||||||
|
color: model.didIReactWithThisEmoji ?
|
||||||
|
(isHovered ? Theme.palette.statusMessage.emojiReactionActiveBackgroundHovered : Theme.palette.statusMessage.emojiReactionActiveBackground) :
|
||||||
|
(isHovered ? Theme.palette.statusMessage.emojiReactionBackgroundHovered : Theme.palette.statusMessage.emojiReactionBackground)
|
||||||
|
|
||||||
|
StatusToolTip {
|
||||||
|
visible: mouseArea.containsMouse
|
||||||
|
maxWidth: 400
|
||||||
|
text: d.showReactionAuthors(model.jsonArrayOfUsersReactedWithThisEmoji, model.emojiId)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rounded corner to cover one corner
|
||||||
|
Rectangle {
|
||||||
|
color: parent.color
|
||||||
|
width: 10
|
||||||
|
height: 10
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: !root.isCurrentUser ? parent.left : undefined
|
||||||
|
anchors.leftMargin: 0
|
||||||
|
anchors.right: !root.isCurrentUser ? undefined : parent.right
|
||||||
|
anchors.rightMargin: 0
|
||||||
|
radius: 2
|
||||||
|
z: -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is a workaround to get a "border" around the rectangle including the weird rectangle
|
||||||
|
Loader {
|
||||||
|
active: model.didIReactWithThisEmoji
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.topMargin: -1
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: -1
|
||||||
|
z: -2
|
||||||
|
|
||||||
|
sourceComponent: Component {
|
||||||
|
Rectangle {
|
||||||
|
width: emojiContainer.width + 2
|
||||||
|
height: emojiContainer.height + 2
|
||||||
|
radius: emojiContainer.radius
|
||||||
|
color: Theme.palette.primaryColor1
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
color: parent.color
|
||||||
|
width: 10
|
||||||
|
height: 10
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: !root.isCurrentUser ? parent.left : undefined
|
||||||
|
anchors.leftMargin: 0
|
||||||
|
anchors.right: !root.isCurrentUser ? undefined : parent.right
|
||||||
|
anchors.rightMargin: 0
|
||||||
|
radius: 2
|
||||||
|
z: -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Use Row
|
||||||
|
|
||||||
|
StatusEmoji {
|
||||||
|
id: emojiImage
|
||||||
|
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: root.imageMargin
|
||||||
|
|
||||||
|
width: 15
|
||||||
|
height: 15
|
||||||
|
|
||||||
|
source: {
|
||||||
|
if (model.emojiId >= 1 && model.emojiId <= root.icons.length)
|
||||||
|
return root.icons[model.emojiId - 1];
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StatusBaseText {
|
||||||
|
id: emojiCount
|
||||||
|
text: model.numberOfReactions
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
anchors.left: emojiImage.right
|
||||||
|
anchors.leftMargin: root.imageMargin
|
||||||
|
font.pixelSize: 12
|
||||||
|
color: model.didIReactWithThisEmoji ? Theme.palette.primaryColor1 : Theme.palette.directColor1
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: mouseArea
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onEntered: {
|
||||||
|
root.hoverChanged(true)
|
||||||
|
}
|
||||||
|
onExited: {
|
||||||
|
root.hoverChanged(false)
|
||||||
|
}
|
||||||
|
onClicked: {
|
||||||
|
root.toggleReaction(model.emojiId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: addEmojiButton.width + addEmojiButton.anchors.leftMargin // there is more margin between the button and the emojis than between each emoji
|
||||||
|
height: addEmojiButton.height
|
||||||
|
|
||||||
|
StatusIcon {
|
||||||
|
id: addEmojiButton
|
||||||
|
|
||||||
|
property bool isHovered: false // TODO: Replace with mouseArea.containsMouse
|
||||||
|
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: 2.5
|
||||||
|
|
||||||
|
icon: "reaction-b"
|
||||||
|
width: 16.5
|
||||||
|
height: 16.5
|
||||||
|
|
||||||
|
color: addEmojiButton.isHovered ? Theme.palette.primaryColor1 : Theme.palette.baseColor1
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: addEmojiButtonMouseArea
|
||||||
|
anchors.fill: addEmojiButton
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
hoverEnabled: true
|
||||||
|
onEntered: addEmojiButton.isHovered = true
|
||||||
|
onExited: addEmojiButton.isHovered = false
|
||||||
|
onClicked: {
|
||||||
|
root.addEmojiClicked(this, mouse);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StatusToolTip {
|
||||||
|
visible: addEmojiButton.isHovered
|
||||||
|
text: qsTr("Add reaction")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,36 +3,39 @@ import QtQuick.Layouts 1.14
|
||||||
|
|
||||||
import StatusQ.Core 0.1
|
import StatusQ.Core 0.1
|
||||||
import StatusQ.Core.Theme 0.1
|
import StatusQ.Core.Theme 0.1
|
||||||
|
import StatusQ.Core.Utils 0.1
|
||||||
import StatusQ.Components 0.1
|
import StatusQ.Components 0.1
|
||||||
import StatusQ.Controls 0.1
|
import StatusQ.Controls 0.1
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: statusMessageHeader
|
id: root
|
||||||
|
|
||||||
|
property StatusMessageSenderDetails sender: StatusMessageSenderDetails { }
|
||||||
|
|
||||||
property alias displayNameLabel: primaryDisplayName
|
property alias displayNameLabel: primaryDisplayName
|
||||||
property alias secondaryNameLabel: secondaryDisplayName
|
property alias secondaryNameLabel: secondaryDisplayName
|
||||||
property alias tertiaryDetailsLabel: tertiaryDetailText
|
property alias tertiaryDetailsLabel: tertiaryDetailText
|
||||||
property alias timestamp: timestampText
|
property alias timestamp: timestampText
|
||||||
|
|
||||||
property string displayName: ""
|
property string tertiaryDetail: sender.id
|
||||||
property string secondaryName: ""
|
|
||||||
property string tertiaryDetail: ""
|
|
||||||
property string resendText: ""
|
property string resendText: ""
|
||||||
property bool showResendButton: false
|
property bool showResendButton: false
|
||||||
property bool isContact: false
|
property bool isContact: sender.isContact
|
||||||
property var trustIndicator: StatusContactVerificationIcons.TrustedType.None
|
property int trustIndicator: sender.trustIndicator
|
||||||
|
property bool amISender: false
|
||||||
|
|
||||||
signal clicked()
|
signal clicked(var sender, var mouse)
|
||||||
signal resendClicked()
|
signal resendClicked()
|
||||||
|
|
||||||
height: childrenRect.height
|
implicitHeight: layout.implicitHeight
|
||||||
width: primaryDisplayName.width + (secondaryDisplayName.visible ? secondaryDisplayName.width + header.spacing : 0)
|
implicitWidth: layout.implicitWidth
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
id: header
|
id: layout
|
||||||
spacing: 4
|
spacing: 4
|
||||||
TextEdit {
|
TextEdit {
|
||||||
id: primaryDisplayName
|
id: primaryDisplayName
|
||||||
|
Layout.alignment: Qt.AlignBottom
|
||||||
font.family: Theme.palette.baseFont.name
|
font.family: Theme.palette.baseFont.name
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
font.pixelSize: 15
|
font.pixelSize: 15
|
||||||
|
@ -41,7 +44,7 @@ Item {
|
||||||
wrapMode: Text.WordWrap
|
wrapMode: Text.WordWrap
|
||||||
selectByMouse: true
|
selectByMouse: true
|
||||||
color: Theme.palette.primaryColor1
|
color: Theme.palette.primaryColor1
|
||||||
text: displayName
|
text: root.amISender ? qsTr("You") : root.sender.displayName
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: mouseArea
|
id: mouseArea
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
@ -49,47 +52,45 @@ Item {
|
||||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
onClicked: {
|
onClicked: {
|
||||||
statusMessageHeader.clicked()
|
root.clicked(this, mouse)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Layout.alignment: Qt.AlignBottom
|
|
||||||
}
|
}
|
||||||
StatusContactVerificationIcons {
|
StatusContactVerificationIcons {
|
||||||
isContact: statusMessageHeader.isContact
|
visible: !root.amISender
|
||||||
trustIndicator: statusMessageHeader.trustIndicator
|
isContact: root.isContact
|
||||||
|
trustIndicator: root.trustIndicator
|
||||||
}
|
}
|
||||||
StatusBaseText {
|
StatusBaseText {
|
||||||
id: secondaryDisplayName
|
id: secondaryDisplayName
|
||||||
Layout.alignment: Qt.AlignVCenter
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
visible: !root.amISender && !!root.sender.secondaryName
|
||||||
color: Theme.palette.baseColor1
|
color: Theme.palette.baseColor1
|
||||||
font.pixelSize: 10
|
font.pixelSize: 10
|
||||||
text: secondaryName
|
text: `(${root.sender.secondaryName})`
|
||||||
visible: !!text
|
|
||||||
}
|
}
|
||||||
StatusBaseText {
|
StatusBaseText {
|
||||||
id: dotSeparator1
|
Layout.alignment: Qt.AlignVCenter
|
||||||
Layout.fillHeight: true
|
visible: secondaryDisplayName.visible
|
||||||
font.pixelSize: 10
|
font.pixelSize: 10
|
||||||
color: Theme.palette.baseColor1
|
color: Theme.palette.baseColor1
|
||||||
text: "."
|
text: "•"
|
||||||
visible: secondaryDisplayName.visible
|
|
||||||
}
|
}
|
||||||
StatusBaseText {
|
StatusBaseText {
|
||||||
id: tertiaryDetailText
|
id: tertiaryDetailText
|
||||||
|
visible: !root.amISender
|
||||||
Layout.alignment: Qt.AlignVCenter
|
Layout.alignment: Qt.AlignVCenter
|
||||||
Layout.maximumWidth: 58
|
|
||||||
font.pixelSize: 10
|
font.pixelSize: 10
|
||||||
elide: Text.ElideMiddle
|
elide: Text.ElideMiddle
|
||||||
color: Theme.palette.baseColor1
|
color: Theme.palette.baseColor1
|
||||||
text: tertiaryDetail
|
text: Utils.elideText(tertiaryDetail, 5, 3)
|
||||||
}
|
}
|
||||||
StatusBaseText {
|
StatusBaseText {
|
||||||
id: dotSeparator2
|
Layout.alignment: Qt.AlignVCenter
|
||||||
Layout.fillHeight: true
|
visible: tertiaryDetailText.visible
|
||||||
font.pixelSize: 10
|
font.pixelSize: 10
|
||||||
color: Theme.palette.baseColor1
|
color: Theme.palette.baseColor1
|
||||||
text: "."
|
text: "•"
|
||||||
visible: tertiaryDetailText.visible
|
|
||||||
}
|
}
|
||||||
StatusTimeStampLabel {
|
StatusTimeStampLabel {
|
||||||
id: timestampText
|
id: timestampText
|
||||||
|
@ -98,12 +99,12 @@ Item {
|
||||||
Layout.alignment: Qt.AlignVCenter
|
Layout.alignment: Qt.AlignVCenter
|
||||||
color: Theme.palette.dangerColor1
|
color: Theme.palette.dangerColor1
|
||||||
font.pixelSize: 12
|
font.pixelSize: 12
|
||||||
text: statusMessageHeader.resendText
|
text: root.resendText
|
||||||
visible: showResendButton && !!timestampText.text
|
visible: showResendButton && !!timestampText.text
|
||||||
MouseArea {
|
MouseArea {
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
onClicked: statusMessageHeader.resendClicked()
|
onClicked: root.resendClicked()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,29 +6,29 @@ import StatusQ.Controls 0.1
|
||||||
import StatusQ.Core.Theme 0.1
|
import StatusQ.Core.Theme 0.1
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: buttonsContainer
|
id: root
|
||||||
|
|
||||||
property list<Item> quickActions
|
property list<Item> items
|
||||||
|
|
||||||
QtObject {
|
QtObject {
|
||||||
id: _internal
|
id: _internal
|
||||||
readonly property int containerMargin: 2
|
readonly property int containerMargin: 2
|
||||||
}
|
}
|
||||||
|
|
||||||
width: buttonRow.width + _internal.containerMargin * 2
|
implicitWidth: buttonRow.width + _internal.containerMargin * 2
|
||||||
height: 36
|
implicitHeight: 36
|
||||||
radius: 8
|
radius: 8
|
||||||
color: Theme.palette.statusSelect.menuItemBackgroundColor
|
color: Theme.palette.statusSelect.menuItemBackgroundColor
|
||||||
|
|
||||||
layer.enabled: true
|
layer.enabled: true
|
||||||
layer.effect: DropShadow {
|
layer.effect: DropShadow {
|
||||||
width: buttonsContainer.width
|
width: root.width
|
||||||
height: buttonsContainer.height
|
height: root.height
|
||||||
x: buttonsContainer.x
|
x: root.x
|
||||||
y: buttonsContainer.y + 10
|
y: root.y + 10
|
||||||
horizontalOffset: 0
|
horizontalOffset: 0
|
||||||
verticalOffset: 2
|
verticalOffset: 2
|
||||||
source: buttonsContainer
|
source: root
|
||||||
radius: 10
|
radius: 10
|
||||||
samples: 15
|
samples: 15
|
||||||
color: Theme.palette.dropShadow
|
color: Theme.palette.dropShadow
|
||||||
|
@ -39,13 +39,13 @@ Rectangle {
|
||||||
spacing: _internal.containerMargin
|
spacing: _internal.containerMargin
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.leftMargin: _internal.containerMargin
|
anchors.leftMargin: _internal.containerMargin
|
||||||
anchors.verticalCenter: buttonsContainer.verticalCenter
|
anchors.verticalCenter: root.verticalCenter
|
||||||
height: parent.height - 2 * _internal.containerMargin
|
height: parent.height - 2 * _internal.containerMargin
|
||||||
}
|
}
|
||||||
|
|
||||||
onQuickActionsChanged: {
|
onItemsChanged: {
|
||||||
for (let idx in quickActions) {
|
for (let idx in items) {
|
||||||
quickActions[idx].parent = buttonRow
|
items[idx].parent = buttonRow
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,18 +7,19 @@ import StatusQ.Core 0.1
|
||||||
import StatusQ.Core.Theme 0.1
|
import StatusQ.Core.Theme 0.1
|
||||||
import StatusQ.Components 0.1
|
import StatusQ.Components 0.1
|
||||||
|
|
||||||
Loader {
|
Item {
|
||||||
id: chatReply
|
id: root
|
||||||
|
|
||||||
property StatusMessageDetails replyDetails
|
property StatusMessageDetails replyDetails
|
||||||
property string audioMessageInfoText: ""
|
property string audioMessageInfoText: ""
|
||||||
|
|
||||||
signal replyProfileClicked()
|
signal replyProfileClicked(var sender, var mouse)
|
||||||
|
|
||||||
active: visible
|
implicitHeight: layout.implicitHeight
|
||||||
|
implicitWidth: layout.implicitWidth
|
||||||
|
|
||||||
sourceComponent: RowLayout {
|
RowLayout {
|
||||||
id: replyLayout
|
id: layout
|
||||||
spacing: 8
|
spacing: 8
|
||||||
Shape {
|
Shape {
|
||||||
id: replyCorner
|
id: replyCorner
|
||||||
|
@ -56,13 +57,16 @@ Loader {
|
||||||
StatusSmartIdenticon {
|
StatusSmartIdenticon {
|
||||||
id: profileImage
|
id: profileImage
|
||||||
Layout.alignment: Qt.AlignTop
|
Layout.alignment: Qt.AlignTop
|
||||||
image: replyDetails.profileImage
|
name: replyDetails.sender.userName
|
||||||
name: replyDetails.displayName
|
image: replyDetails.sender.profileImage.imageSettings
|
||||||
|
icon: replyDetails.sender.profileImage.iconSettings
|
||||||
|
ringSettings: replyDetails.sender.profileImage.ringSettings
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
onClicked: replyProfileClicked()
|
onClicked: replyProfileClicked(this, mouse)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TextEdit {
|
TextEdit {
|
||||||
|
@ -74,7 +78,7 @@ Loader {
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
selectByMouse: true
|
selectByMouse: true
|
||||||
readOnly: true
|
readOnly: true
|
||||||
text: replyDetails.displayName
|
text: replyDetails.amISender ? qsTr("You") : replyDetails.sender.displayName
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
StatusTextMessage {
|
StatusTextMessage {
|
||||||
|
@ -85,13 +89,14 @@ Loader {
|
||||||
textField.height: 18
|
textField.height: 18
|
||||||
clip: true
|
clip: true
|
||||||
visible: !!replyDetails.messageText
|
visible: !!replyDetails.messageText
|
||||||
|
allowShowMore: false
|
||||||
}
|
}
|
||||||
StatusImageMessage {
|
StatusImageMessage {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.preferredHeight: imageAlias.paintedHeight
|
Layout.preferredHeight: imageAlias.paintedHeight
|
||||||
imageWidth: 56
|
imageWidth: 56
|
||||||
source: replyDetails.contentType === StatusMessage.ContentType.Image ? replyDetails.messageContent : ""
|
source: replyDetails.contentType === StatusMessage.ContentType.Image ? replyDetails.messageContent : ""
|
||||||
visible: replyDetails.contentType === StatusMessage.ContentType.Image
|
// visible: replyDetails.contentType === StatusMessage.ContentType.Image
|
||||||
shapeType: StatusImageMessage.ShapeType.ROUNDED
|
shapeType: StatusImageMessage.ShapeType.ROUNDED
|
||||||
}
|
}
|
||||||
Item {
|
Item {
|
||||||
|
@ -116,7 +121,7 @@ Loader {
|
||||||
height: 22
|
height: 22
|
||||||
isPreview: true
|
isPreview: true
|
||||||
audioSource: replyDetails.messageContent
|
audioSource: replyDetails.messageContent
|
||||||
audioMessageInfoText: chatReply.audioMessageInfoText
|
audioMessageInfoText: root.audioMessageInfoText
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import QtQuick 2.13
|
import QtQuick 2.13
|
||||||
|
import QtQuick.Controls 2.14
|
||||||
import QtQuick.Layouts 1.14
|
import QtQuick.Layouts 1.14
|
||||||
import QtGraphicalEffects 1.13
|
import QtGraphicalEffects 1.13
|
||||||
|
|
||||||
|
@ -11,14 +12,34 @@ Loader {
|
||||||
|
|
||||||
active: visible
|
active: visible
|
||||||
|
|
||||||
sourceComponent: Rectangle {
|
sourceComponent: Control {
|
||||||
height: 24
|
verticalPadding: 3
|
||||||
width: layout.width + 16
|
leftPadding: 2
|
||||||
color: Theme.palette.pinColor2
|
rightPadding: 6
|
||||||
radius: 12
|
|
||||||
RowLayout {
|
background: Rectangle {
|
||||||
id: layout
|
readonly property color translucentColor: Theme.palette.pinColor2
|
||||||
anchors.centerIn: parent
|
|
||||||
|
implicitWidth: 24
|
||||||
|
implicitHeight: 24
|
||||||
|
color: Qt.rgba(translucentColor.r,
|
||||||
|
translucentColor.g,
|
||||||
|
translucentColor.b, 1)
|
||||||
|
opacity: translucentColor.a
|
||||||
|
layer.enabled: true
|
||||||
|
radius: 12
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.left: parent.left
|
||||||
|
width: parent.width / 2
|
||||||
|
height: parent.height / 2
|
||||||
|
color: parent.color
|
||||||
|
radius: 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
contentItem: RowLayout {
|
||||||
StatusIcon {
|
StatusIcon {
|
||||||
Layout.alignment: Qt.AlignVCenter
|
Layout.alignment: Qt.AlignVCenter
|
||||||
Layout.preferredWidth: 16
|
Layout.preferredWidth: 16
|
||||||
|
|
|
@ -6,24 +6,33 @@ import StatusQ.Controls 0.1
|
||||||
import StatusQ.Core.Theme 0.1
|
import StatusQ.Core.Theme 0.1
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: textMessage
|
id: root
|
||||||
|
|
||||||
property int contentType: 0
|
|
||||||
property alias textField: chatText
|
property alias textField: chatText
|
||||||
|
property bool allowShowMore: true
|
||||||
|
|
||||||
signal linkActivated(url link)
|
signal linkActivated(string link)
|
||||||
|
|
||||||
implicitHeight: showMoreLoader.active ? childrenRect.height : chatText.height
|
implicitWidth: chatText.implicitWidth
|
||||||
|
implicitHeight: chatText.effectiveHeight + d.showMoreHeight
|
||||||
|
|
||||||
QtObject {
|
QtObject {
|
||||||
id: _internal
|
id: d
|
||||||
property bool readMore: false
|
property bool readMore: false
|
||||||
property bool veryLongChatText: chatText.length > 1000
|
readonly property bool veryLongChatText: chatText.length > 1000
|
||||||
|
readonly property int showMoreHeight: showMoreLoader.visible ? showMoreLoader.height : 0
|
||||||
}
|
}
|
||||||
|
|
||||||
TextEdit {
|
TextEdit {
|
||||||
id: chatText
|
id: chatText
|
||||||
visible: !showMoreLoader.active || _internal.readMore
|
|
||||||
|
readonly property int effectiveHeight: d.veryLongChatText && !d.readMore ? Math.min(chatText.implicitHeight, 200)
|
||||||
|
: chatText.implicitHeight
|
||||||
|
|
||||||
|
width: parent.width
|
||||||
|
height: effectiveHeight + d.showMoreHeight / 2
|
||||||
|
visible: !opMask.active
|
||||||
|
clip: true
|
||||||
selectedTextColor: Theme.palette.directColor1
|
selectedTextColor: Theme.palette.directColor1
|
||||||
selectionColor: Theme.palette.primaryColor3
|
selectionColor: Theme.palette.primaryColor3
|
||||||
color: Theme.palette.directColor1
|
color: Theme.palette.directColor1
|
||||||
|
@ -33,12 +42,12 @@ Item {
|
||||||
wrapMode: Text.Wrap
|
wrapMode: Text.Wrap
|
||||||
readOnly: true
|
readOnly: true
|
||||||
selectByMouse: true
|
selectByMouse: true
|
||||||
height: _internal.veryLongChatText && !_internal.readMore ? Math.min(implicitHeight, 200) : implicitHeight
|
onLinkActivated: {
|
||||||
width: parent.width
|
root.linkActivated(link);
|
||||||
clip: height < implicitHeight
|
}
|
||||||
onLinkActivated: textMessage.linkActivated(link)
|
|
||||||
onLinkHovered: {
|
onLinkHovered: {
|
||||||
cursorShape: Qt.PointingHandCursor
|
// Strange thing. Without this empty stub the cursorShape
|
||||||
|
// is not changed to pointingHandCursor.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,7 +69,7 @@ Item {
|
||||||
|
|
||||||
Loader {
|
Loader {
|
||||||
id: opMask
|
id: opMask
|
||||||
active: showMoreLoader.active && !_internal.readMore
|
active: showMoreLoader.active && !d.readMore
|
||||||
anchors.fill: chatText
|
anchors.fill: chatText
|
||||||
sourceComponent: OpacityMask {
|
sourceComponent: OpacityMask {
|
||||||
source: chatText
|
source: chatText
|
||||||
|
@ -70,17 +79,17 @@ Item {
|
||||||
|
|
||||||
Loader {
|
Loader {
|
||||||
id: showMoreLoader
|
id: showMoreLoader
|
||||||
active: _internal.veryLongChatText
|
active: root.allowShowMore && d.veryLongChatText
|
||||||
anchors.top: chatText.bottom
|
visible: active
|
||||||
anchors.topMargin: -10
|
anchors.verticalCenter: chatText.bottom
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
sourceComponent: StatusRoundButton {
|
sourceComponent: StatusRoundButton {
|
||||||
implicitWidth: 24
|
implicitWidth: 24
|
||||||
implicitHeight: 24
|
implicitHeight: 24
|
||||||
type: StatusRoundButton.Type.Secondary
|
type: StatusRoundButton.Type.Secondary
|
||||||
icon.name: _internal.readMore ? "chevron-up": "chevron-down"
|
icon.name: d.readMore ? "chevron-up": "chevron-down"
|
||||||
onClicked: {
|
onClicked: {
|
||||||
_internal.readMore = !_internal.readMore
|
d.readMore = !d.readMore
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ StatusChatToolBar 0.1 StatusChatToolBar.qml
|
||||||
StatusContactRequestsIndicatorListItem 0.1 StatusContactRequestsIndicatorListItem.qml
|
StatusContactRequestsIndicatorListItem 0.1 StatusContactRequestsIndicatorListItem.qml
|
||||||
StatusEmoji 0.1 StatusEmoji.qml
|
StatusEmoji 0.1 StatusEmoji.qml
|
||||||
StatusContactVerificationIcons 0.1 StatusContactVerificationIcons.qml
|
StatusContactVerificationIcons 0.1 StatusContactVerificationIcons.qml
|
||||||
|
StatusDateGroupLabel 0.1 StatusDateGroupLabel.qml
|
||||||
StatusDescriptionListItem 0.1 StatusDescriptionListItem.qml
|
StatusDescriptionListItem 0.1 StatusDescriptionListItem.qml
|
||||||
StatusLetterIdenticon 0.1 StatusLetterIdenticon.qml
|
StatusLetterIdenticon 0.1 StatusLetterIdenticon.qml
|
||||||
StatusListItem 0.1 StatusListItem.qml
|
StatusListItem 0.1 StatusListItem.qml
|
||||||
|
@ -31,6 +32,7 @@ StatusExpandableItem 0.1 StatusExpandableItem.qml
|
||||||
StatusSmartIdenticon 0.1 StatusSmartIdenticon.qml
|
StatusSmartIdenticon 0.1 StatusSmartIdenticon.qml
|
||||||
StatusMessage 0.1 StatusMessage.qml
|
StatusMessage 0.1 StatusMessage.qml
|
||||||
StatusMessageDetails 0.1 StatusMessageDetails.qml
|
StatusMessageDetails 0.1 StatusMessageDetails.qml
|
||||||
|
StatusMessageSenderDetails 0.1 StatusMessageSenderDetails.qml
|
||||||
StatusTagSelector 0.1 StatusTagSelector.qml
|
StatusTagSelector 0.1 StatusTagSelector.qml
|
||||||
StatusToastMessage 0.1 StatusToastMessage.qml
|
StatusToastMessage 0.1 StatusToastMessage.qml
|
||||||
StatusWizardStepper 0.1 StatusWizardStepper.qml
|
StatusWizardStepper 0.1 StatusWizardStepper.qml
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
import QtQuick 2.0
|
||||||
|
import StatusQ.Core.Theme 0.1
|
||||||
|
|
||||||
|
QtObject {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property url source
|
||||||
|
property int width
|
||||||
|
property int height
|
||||||
|
property bool isIdenticon: false
|
||||||
|
|
||||||
|
property string name
|
||||||
|
property string pubkey
|
||||||
|
property string image
|
||||||
|
property bool showRing: true
|
||||||
|
property bool interactive: true
|
||||||
|
|
||||||
|
property int colorId // TODO: default value Utils.colorIdForPubkey(pubkey)
|
||||||
|
property var colorHash // TODO: default value Utils.getColorHashAsJson(pubkey)
|
||||||
|
|
||||||
|
property StatusImageSettings imageSettings: StatusImageSettings {
|
||||||
|
width: root.width
|
||||||
|
height: root.height
|
||||||
|
source: root.source
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly property StatusIconSettings iconSettings: StatusIconSettings {
|
||||||
|
width: root.width
|
||||||
|
height: root.height
|
||||||
|
color: Theme.palette.userCustomizationColors[root.colorId]
|
||||||
|
charactersLen: 2
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly property StatusIdenticonRingSettings ringSettings: StatusIdenticonRingSettings {
|
||||||
|
initalAngleRad: 0
|
||||||
|
ringPxSize: Math.max(1.5, root.width / 24.0)
|
||||||
|
ringSpecModel: root.showRing ? root.colorHash : undefined
|
||||||
|
distinctiveColors: Theme.palette.identiconRingColors
|
||||||
|
}
|
||||||
|
}
|
|
@ -154,5 +154,12 @@ ThemePalette {
|
||||||
property color menuItemBackgroundColor: baseColor2
|
property color menuItemBackgroundColor: baseColor2
|
||||||
property color menuItemHoverBackgroundColor: directColor7
|
property color menuItemHoverBackgroundColor: directColor7
|
||||||
}
|
}
|
||||||
|
|
||||||
|
property QtObject statusMessage: QtObject {
|
||||||
|
property color emojiReactionBackground: "#2d2823"
|
||||||
|
property color emojiReactionBackgroundHovered: "#3a3632"
|
||||||
|
property color emojiReactionActiveBackground: "#353a4d"
|
||||||
|
property color emojiReactionActiveBackgroundHovered: "#cbd5f1"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -152,5 +152,12 @@ ThemePalette {
|
||||||
property color menuItemBackgroundColor: white
|
property color menuItemBackgroundColor: white
|
||||||
property color menuItemHoverBackgroundColor: baseColor2
|
property color menuItemHoverBackgroundColor: baseColor2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
property QtObject statusMessage: QtObject {
|
||||||
|
property color emojiReactionBackground: "#e2e6e9"
|
||||||
|
property color emojiReactionBackgroundHovered: "#d7dadd"
|
||||||
|
property color emojiReactionActiveBackground: getColor('blue6')
|
||||||
|
property color emojiReactionActiveBackgroundHovered: "#cbd5f1"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -243,7 +243,8 @@ QtObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
function getColor(name, alpha) {
|
function getColor(name, alpha) {
|
||||||
return !!alpha ? alphaColor(StatusColors.colors[name], alpha) : StatusColors.colors[name]
|
return !!alpha ? alphaColor(StatusColors.colors[name], alpha)
|
||||||
|
: StatusColors.colors[name]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
pragma Singleton
|
pragma Singleton
|
||||||
|
|
||||||
import QtQuick 2.13
|
import QtQuick 2.13
|
||||||
|
import StatusQ.Core.Theme 0.1
|
||||||
|
import "./xss.js" as XSS
|
||||||
|
|
||||||
QtObject {
|
QtObject {
|
||||||
|
|
||||||
|
@ -151,6 +153,71 @@ QtObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function linkifyAndXSS(inputText) {
|
||||||
|
//URLs starting with http://, https://, or ftp://
|
||||||
|
var replacePattern1 = /(\b(https?|ftp|statusim):\/\/[-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>");
|
||||||
|
|
||||||
|
return XSS.filterXSS(replacedText)
|
||||||
|
}
|
||||||
|
|
||||||
|
function filterXSS(inputText) {
|
||||||
|
return XSS.filterXSS(inputText)
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMessageWithStyle(msg, hoveredLink = "") {
|
||||||
|
return `<style type="text/css">` +
|
||||||
|
`img, a, del, code, blockquote { margin: 0; padding: 0; }` +
|
||||||
|
`code {` +
|
||||||
|
`font-family: ${Theme.palette.codeFont.name};` +
|
||||||
|
`font-weight: 400;` +
|
||||||
|
`font-size: 14;` +
|
||||||
|
`padding: 2px 4px;` +
|
||||||
|
`border-radius: 4px;` +
|
||||||
|
`background-color: ${Theme.palette.baseColor2};` +
|
||||||
|
`color: ${Theme.palette.directColor1};` +
|
||||||
|
`white-space: pre;` +
|
||||||
|
`}` +
|
||||||
|
`p {` +
|
||||||
|
`line-height: 22px;` +
|
||||||
|
`}` +
|
||||||
|
`a {` +
|
||||||
|
`color: ${Theme.palette.primaryColor1};` +
|
||||||
|
`}` +
|
||||||
|
`a.mention {` +
|
||||||
|
`color: ${Theme.palette.mentionColor1};` +
|
||||||
|
`background-color: ${Theme.palette.mentionColor4};` +
|
||||||
|
`text-decoration: none;` +
|
||||||
|
`padding: 0px 2px;` +
|
||||||
|
`}` +
|
||||||
|
(hoveredLink !== "" ? `a.mention[href="${hoveredLink}"] { background-color: ${Theme.palette.mentionColor2}; }` : ``) +
|
||||||
|
`del {` +
|
||||||
|
`text-decoration: line-through;` +
|
||||||
|
`}` +
|
||||||
|
`table.blockquote td {` +
|
||||||
|
`padding-left: 10px;` +
|
||||||
|
`color: ${Theme.palette.baseColor1};` +
|
||||||
|
`}` +
|
||||||
|
`table.blockquote td.quoteline {` +
|
||||||
|
`background-color: ${Theme.palette.baseColor1};` +
|
||||||
|
`height: 100%;` +
|
||||||
|
`padding-left: 0;` +
|
||||||
|
`}` +
|
||||||
|
`.emoji {` +
|
||||||
|
`vertical-align: bottom;` +
|
||||||
|
`}` +
|
||||||
|
`span.isEdited {` +
|
||||||
|
`color: ${Theme.palette.baseColor1};` +
|
||||||
|
`margin-left: 5px` +
|
||||||
|
`}` +
|
||||||
|
`</style>` +
|
||||||
|
`${msg}`
|
||||||
|
}
|
||||||
|
|
||||||
function delegateModelSort(srcGroup, dstGroup, lessThan) {
|
function delegateModelSort(srcGroup, dstGroup, lessThan) {
|
||||||
const insertPosition = (lessThan, item) => {
|
const insertPosition = (lessThan, item) => {
|
||||||
let lower = 0
|
let lower = 0
|
||||||
|
@ -173,6 +240,10 @@ QtObject {
|
||||||
dstGroup.move(item.itemsIndex, index)
|
dstGroup.move(item.itemsIndex, index)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function elideText(text, leftCharsCount, rightCharsCount = leftCharsCount) {
|
||||||
|
return text.substr(0, leftCharsCount) + "..." + text.substr(text.length - rightCharsCount)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
module StatusQ.Core.Utils
|
module StatusQ.Core.Utils
|
||||||
|
|
||||||
|
EmojiJSON 1.0 emojiList.js
|
||||||
|
XSS 1.0 xss.js
|
||||||
singleton Utils 0.1 Utils.qml
|
singleton Utils 0.1 Utils.qml
|
||||||
singleton Emoji 0.1 Emoji.qml
|
singleton Emoji 0.1 Emoji.qml
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -14,3 +14,4 @@ StatusAnimatedStack 0.1 StatusAnimatedStack.qml
|
||||||
StatusScrollView 0.1 StatusScrollView.qml
|
StatusScrollView 0.1 StatusScrollView.qml
|
||||||
StatusListView 0.1 StatusListView.qml
|
StatusListView 0.1 StatusListView.qml
|
||||||
StatusGridView 0.1 StatusGridView.qml
|
StatusGridView 0.1 StatusGridView.qml
|
||||||
|
StatusProfileImageSettings 0.1 StatusProfileImageSettings.qml
|
||||||
|
|
Loading…
Reference in New Issue