status-desktop/ui/app/AppLayouts/Chat/ChatColumn/MessageComponents/EmojiReactions.qml

235 lines
8.7 KiB
QML

import QtQuick 2.3
import QtQuick.Controls 2.13
import QtGraphicalEffects 1.13
import "../../../../../shared"
import "../../../../../shared/status"
import "../../../../../imports"
Item {
property int imageMargin: 4
signal hoverChanged(bool hovered)
id: root
height: 20
width: childrenRect.width
function lastTwoItems(nodes) {
return nodes.join(qsTr(" and "));
}
function showReactionAuthors(fromAccounts) {
if (fromAccounts.length < 3)
return lastTwoItems(fromAccounts);
var leftNode = [];
var rightNode = [];
if (fromAccounts.length > 3) {
leftNode = fromAccounts.slice(0, 3);
rightNode = fromAccounts.slice(3, fromAccounts.length);
return (rightNode.length === 1) ?
lastTwoItems([leftNode.join(", "), rightNode[0]]) :
lastTwoItems([leftNode.join(", "), qsTr("%1 more reacted.").arg(rightNode.length)]);
}
leftNode = fromAccounts.slice(0, 2);
rightNode = fromAccounts.slice(2, fromAccounts.length);
return lastTwoItems([leftNode.join(", "), rightNode[0]]);
}
Row {
spacing: root.imageMargin
Repeater {
id: reactionRepeater
width: childrenRect.width
model: {
if (!emojiReactions) {
return []
}
try {
// group by id
var allReactions = Object.values(JSON.parse(emojiReactions))
var byEmoji = {}
allReactions.forEach(function (reaction) {
if (!byEmoji[reaction.emojiId]) {
byEmoji[reaction.emojiId] = {
emojiId: reaction.emojiId,
fromAccounts: [],
count: 0,
currentUserReacted: false
}
}
byEmoji[reaction.emojiId].count++;
byEmoji[reaction.emojiId].fromAccounts.push(chatsModel.userNameOrAlias(reaction.from));
if (!byEmoji[reaction.emojiId].currentUserReacted && reaction.from === profileModel.profile.pubKey) {
byEmoji[reaction.emojiId].currentUserReacted = true
}
})
return Object.values(byEmoji)
} catch (e) {
console.error('Error parsing emoji reactions', e)
return []
}
}
Rectangle {
property bool isHovered: false
id: emojiContainer
width: emojiImage.width + emojiCount.width + (root.imageMargin * 2) + + 8
height: 20
radius: 10
color: modelData.currentUserReacted ? Style.current.secondaryBackground :
(isHovered ? Style.current.emojiReactionBackgroundHovered : Style.current.emojiReactionBackground)
ToolTip {
visible: mouseArea.containsMouse
text: showReactionAuthors(modelData.fromAccounts)
}
// Rounded corner to cover one corner
Rectangle {
color: parent.color
width: 8
height: 8
anchors.top: parent.top
anchors.left: !isCurrentUser || appSettings.compactMode ? parent.left : undefined
anchors.leftMargin: 0
anchors.right: !isCurrentUser || appSettings.compactMode ? 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: modelData.currentUserReacted
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: Style.current.primary
Rectangle {
color: parent.color
width: 8
height: 8
anchors.top: parent.top
anchors.left: !isCurrentUser || appSettings.compactMode ? parent.left : undefined
anchors.leftMargin: 0
anchors.right: !isCurrentUser || appSettings.compactMode ? undefined : parent.right
anchors.rightMargin: 0
radius: 2
z: -1
}
}
}
}
SVGImage {
id: emojiImage
width: 15
fillMode: Image.PreserveAspectFit
source: {
const basePath = "../../../../img/emojiReactions/"
switch (modelData.emojiId) {
case 1: return basePath + "heart.svg"
case 2: return basePath + "thumbsUp.svg"
case 3: return basePath + "thumbsDown.svg"
case 4: return basePath + "laughing.svg"
case 5: return basePath + "sad.svg"
case 6: return basePath + "angry.svg"
default: return ""
}
}
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: root.imageMargin
}
StyledText {
id: emojiCount
text: modelData.count
anchors.verticalCenter: parent.verticalCenter
anchors.left: emojiImage.right
anchors.leftMargin: root.imageMargin
font.pixelSize: 12
color: modelData.currentUserReacted ? Style.current.textColorTertiary : Style.current.textColor
}
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onEntered: {
root.hoverChanged(true)
emojiContainer.isHovered = true
}
onExited: {
root.hoverChanged(false)
emojiContainer.isHovered = false
}
onClicked: {
chatsModel.toggleReaction(messageId, modelData.emojiId)
}
}
}
}
Item {
width: addEmojiBtn.width + addEmojiBtn.anchors.leftMargin // there is more margin between the button and the emojis than between each emoji
height: addEmojiBtn.height
SVGImage {
property bool isHovered: false
id: addEmojiBtn
source: "../../../../img/emoji.svg"
width: 16.5
height: 16.5
anchors.left: parent.left
anchors.leftMargin: 2.5
}
ColorOverlay {
anchors.fill: addEmojiBtn
antialiasing: true
source: addEmojiBtn
color: addEmojiBtn.isHovered ? Style.current.primary : Style.current.secondaryText
}
MouseArea {
anchors.fill: addEmojiBtn
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
onEntered: addEmojiBtn.isHovered = true
onExited: addEmojiBtn.isHovered = false
onClicked: {
isMessageActive = true
clickMessage(false, false, false, null, true)
}
}
StatusToolTip {
visible: addEmojiBtn.isHovered
text: qsTr("Add reaction")
width: 115
}
}
}
}