Skip to content
Snippets Groups Projects
Commit d94c3083 authored by Andreas Traczyk's avatar Andreas Traczyk Committed by Adrien Béraud
Browse files

chatview: fix emoji responses under Qt v 6.4.x and up

Qt.bindings are not supported for properties within when calling createObject anymore. Instead, we can pass component references, or generate bindings post-creation.

Note: in this case, the popup closes and observation is not really needed here.

Gitlab: #1169
Change-Id: I21c1d95f4ca725c86fcf276834830145587458cc
parent ed6e76aa
No related branches found
No related tags found
No related merge requests found
......@@ -30,6 +30,9 @@ install/
*.log
*.pid
# tests
Testing/
# auto-gen files
src/app/resources.qrc
src/app/qml.qrc
......
......@@ -376,11 +376,19 @@ def run_tests(config_str, qt_dir):
qt_dir, 'bin', 'QtWebEngineProcess.exe')
os.environ["QML2_IMPORT_PATH"] = os.path.join(qt_dir, "qml")
cmd = ["ctest", "-V", "-C", config_str]
# On Windows, when running on a jenkins slave, the QML tests don't output
# anything to stdout/stderr. Workaround by outputting to a file and then
# printing the contents of the file.
if os.environ.get("JENKINS_URL"):
cmd += ["--output-log", "test.log", "--quiet"]
tests_dir = os.path.join(build_dir, "tests")
if execute_cmd(["ctest", "-V", "-C", config_str],
False, None, tests_dir):
print("Tests failed.")
sys.exit(1)
exit_code = execute_cmd(cmd, False, None, tests_dir)
# Print the contents of the log file.
if os.environ.get("JENKINS_URL"):
with open(os.path.join(tests_dir, "test.log"), "r") as file:
print(file.read())
sys.exit(exit_code)
def generate_msi(version):
......
......@@ -31,7 +31,7 @@ Popup {
background.visible: false
parent: Overlay.overlay
property var emojiReaction
property var reactions
property string msgId
// center in parent
......@@ -88,9 +88,9 @@ Popup {
spacing: 15
Layout.preferredWidth: 400
Layout.preferredHeight: childrenRect.height + 30 < 700 ? childrenRect.height + 30 : 700
model: Object.entries(emojiReaction)
model: Object.entries(reactions)
clip: true
property int modelCount: Object.entries(emojiReaction).length
property int modelCount: Object.entries(reactions).length
delegate: RowLayout {
width: parent.width
......
......@@ -24,19 +24,20 @@ import net.jami.Constants 1.1
Item {
id: root
property var emojiReaction
property var reactions
property real contentHeight: bubble.height
property real contentWidth: bubble.width
property var emojiTexts: ownEmojiList
visible: emojis.length && Body !== ""
property string emojis: {
if (reactions === undefined)
return [];
var space = "";
var emojiList = [];
var emojiNumberList = [];
for (const reactions of Object.entries(emojiReaction)) {
var authorEmojiList = reactions[1];
for (const reaction of Object.entries(reactions)) {
var authorEmojiList = reaction[1];
for (var emojiIndex in authorEmojiList) {
var emoji = authorEmojiList[emojiIndex];
if (emojiList.includes(emoji)) {
......@@ -60,12 +61,14 @@ Item {
return cur;
}
property var ownEmojiList: {
property var ownEmojis: {
if (reactions === undefined)
return [];
var list = [];
var index = 0;
for (const reactions of Object.entries(emojiReaction)) {
var authorUri = reactions[0];
var authorEmojiList = reactions[1];
for (const reaction of Object.entries(reactions)) {
var authorUri = reaction[0];
var authorEmojiList = reaction[1];
if (CurrentAccount.uri === authorUri) {
for (var emojiIndex in authorEmojiList) {
list[index] = authorEmojiList[emojiIndex];
......
......@@ -33,9 +33,11 @@ Popup {
padding: 0
background.visible: false
required property var emojiReactions
property var emojiReplied: emojiReactions.ownEmojis
required property string msgId
required property string msgBody
required property var emojiReplied
required property bool isOutgoing
required property int type
required property string transferName
......@@ -107,27 +109,11 @@ Popup {
onClosed: if (emojiPicker) emojiPicker.closeEmojiPicker()
function getModel() {
var model = ["👍", "👎", "😂"]
var cur = []
//Add emoji reacted
var index = 0
for (let emoji of emojiReplied) {
if (index < model.length) {
cur[index] = emoji
index ++
}
}
//complete with default model
var modelIndex = cur.length
for (let j = 0; j < model.length; j++) {
if (cur.length < model.length) {
if (!cur.includes(model[j]) ) {
cur[modelIndex] = model[j]
modelIndex ++
}
}
}
return cur
const defaultModel = ["👍", "👎", "😂"]
const reactedEmojis = Array.isArray(emojiReplied) ? emojiReplied.slice(0, defaultModel.length) : []
const uniqueEmojis = Array.from(new Set(reactedEmojis))
const missingEmojis = defaultModel.filter(emoji => !uniqueEmojis.includes(emoji))
return uniqueEmojis.concat(missingEmojis)
}
Rectangle {
......@@ -167,7 +153,7 @@ Popup {
background: Rectangle {
anchors.fill: parent
opacity: emojiReplied.includes(modelData) ? 1 : 0
opacity: emojiReplied ? (emojiReplied.includes(modelData) ? 1 : 0) : 0
color: JamiTheme.emojiReactPushButtonColor
radius: 10
}
......
......@@ -15,12 +15,10 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Qt5Compat.GraphicalEffects
import net.jami.Models 1.1
import net.jami.Adapters 1.1
import net.jami.Constants 1.1
......@@ -64,9 +62,7 @@ Control {
// If the ListView attached properties are not available,
// then the root delegate is likely a Loader.
readonly property ListView listView: ListView.view ?
ListView.view :
parent.ListView.view
readonly property ListView listView: ListView.view ? ListView.view : parent.ListView.view
rightPadding: hPadding
leftPadding: hPadding
......@@ -99,7 +95,7 @@ Control {
id: username
text: UtilsAdapter.getBestNameForUri(CurrentAccount.id, Author)
font.bold: true
visible:(seq === MsgSeq.first || seq === MsgSeq.single) && !isOutgoing
visible: (seq === MsgSeq.first || seq === MsgSeq.single) && !isOutgoing
font.pixelSize: JamiTheme.usernameBlockFontSize
color: JamiTheme.chatviewUsernameColor
lineHeight: JamiTheme.usernameBlockLineHeight
......@@ -108,7 +104,6 @@ Control {
}
}
Item {
id: replyItem
property bool isSelf: ReplyToAuthor === CurrentAccount.uri
......@@ -123,14 +118,15 @@ Control {
Layout.leftMargin: isOutgoing ? undefined : JamiTheme.sbsMessageBaseReplyMargin
Layout.rightMargin: !isOutgoing ? undefined : JamiTheme.sbsMessageBaseReplyMargin
transform: Translate { y: JamiTheme.sbsMessageBaseReplyBottomMargin }
transform: Translate {
y: JamiTheme.sbsMessageBaseReplyBottomMargin
}
ColumnLayout {
width: parent.width
spacing: 2
RowLayout{
RowLayout {
id: replyToLayout
Layout.alignment: isOutgoing ? Qt.AlignRight : Qt.AlignLeft
......@@ -155,8 +151,8 @@ Control {
showPresenceIndicator: false
imageId: {
if (replyItem.isSelf)
return CurrentAccount.id
return ReplyToAuthor
return CurrentAccount.id;
return ReplyToAuthor;
}
mode: replyItem.isSelf ? Avatar.Mode.Account : Avatar.Mode.Contact
}
......@@ -179,11 +175,10 @@ Control {
color: replyItem.isSelf ? CurrentConversation.color : JamiTheme.messageInBgColor
radius: msgRadius
Layout.preferredWidth: replyToRow.width + 2*JamiTheme.preferredMarginSize
Layout.preferredHeight: replyToRow.height + 2*JamiTheme.preferredMarginSize
Layout.preferredWidth: replyToRow.width + 2 * JamiTheme.preferredMarginSize
Layout.preferredHeight: replyToRow.height + 2 * JamiTheme.preferredMarginSize
Layout.alignment: isOutgoing ? Qt.AlignRight : Qt.AlignLeft
// place actual content here
ReplyToRow {
id: replyToRow
......@@ -194,8 +189,8 @@ Control {
MouseArea {
z: 2
anchors.fill: parent
onClicked: function(mouse) {
CurrentConversation.scrollToMsg(ReplyTo)
onClicked: function (mouse) {
CurrentConversation.scrollToMsg(ReplyTo);
}
}
}
......@@ -211,7 +206,7 @@ Control {
Item {
id: avatarBlock
Layout.preferredWidth: isOutgoing ? 0 : avatar.width + hPadding/3
Layout.preferredWidth: isOutgoing ? 0 : avatar.width + hPadding / 3
Layout.preferredHeight: isOutgoing ? 0 : bubble.height
Avatar {
id: avatar
......@@ -238,7 +233,7 @@ Control {
hoverEnabled: true
onClicked: function (mouse) {
if (root.hoveredLink) {
MessagesAdapter.openUrl(root.hoveredLink)
MessagesAdapter.openUrl(root.hoveredLink);
}
}
property bool bubbleHovered: containsMouse || textHovered
......@@ -276,30 +271,24 @@ Control {
anchors.verticalCenter: parent.verticalCenter
anchors.right: isOutgoing ? optionButtonItem.right : undefined
anchors.left: !isOutgoing ? optionButtonItem.left : undefined
visible: CurrentAccount.type !== Profile.Type.SIP && Body !== "" &&
(
bubbleArea.bubbleHovered
|| hovered
|| reply.hovered
|| bgHandler.hovered
)
visible: CurrentAccount.type !== Profile.Type.SIP && Body !== "" && (bubbleArea.bubbleHovered || hovered || reply.hovered || bgHandler.hovered)
source: JamiResources.more_vert_24dp_svg
width: optionButtonItem.width / 2
height: optionButtonItem.height
onClicked: {
var component = Qt.createComponent("qrc:/commoncomponents/MessageOptionsPopup.qml")
var component = Qt.createComponent("qrc:/commoncomponents/MessageOptionsPopup.qml");
var obj = component.createObject(bubble, {
"emojiReplied": Qt.binding(() => emojiReaction.emojiTexts),
"isOutgoing": isOutgoing,
"msgId": Id,
"msgBody": Body,
"type": Type,
"transferName": TransferName,
"msgBubble": bubble,
"listView": listView
})
obj.open()
"emojiReactions": emojiReactions,
"isOutgoing": isOutgoing,
"msgId": Id,
"msgBody": Body,
"type": Type,
"transferName": TransferName,
"msgBubble": bubble,
"listView": listView
});
obj.open();
}
}
......@@ -315,17 +304,11 @@ Control {
anchors.verticalCenter: parent.verticalCenter
anchors.right: isOutgoing ? more.left : undefined
anchors.left: !isOutgoing ? more.right : undefined
visible: CurrentAccount.type !== Profile.Type.SIP && Body !== "" &&
(
bubbleArea.bubbleHovered
|| hovered
|| more.hovered
|| bgHandler.hovered
)
visible: CurrentAccount.type !== Profile.Type.SIP && Body !== "" && (bubbleArea.bubbleHovered || hovered || more.hovered || bgHandler.hovered)
onClicked: {
MessagesAdapter.editId = ""
MessagesAdapter.replyToId = Id
MessagesAdapter.editId = "";
MessagesAdapter.replyToId = Id;
}
}
}
......@@ -335,18 +318,18 @@ Control {
property bool isEdited: PreviousBodies.length !== 0
visible: !IsEmojiOnly
z:-1
z: -1
out: isOutgoing
type: seq
isReply: root.isReply
function getBaseColor() {
var baseColor = isOutgoing ? CurrentConversation.color : JamiTheme.messageInBgColor
var baseColor = isOutgoing ? CurrentConversation.color : JamiTheme.messageInBgColor;
if (Id === MessagesAdapter.replyToId || Id === MessagesAdapter.editId) {
// If we are replying to or editing the message
return Qt.darker(baseColor, 1.5)
return Qt.darker(baseColor, 1.5);
}
return baseColor
return baseColor;
}
color: getBaseColor()
......@@ -358,7 +341,6 @@ Control {
height: innerContent.childrenRect.height + (visible ? root.extraHeight : 0)
}
Rectangle {
id: bg
......@@ -412,8 +394,8 @@ Control {
target: CurrentConversation
function onScrollTo(id) {
if (id !== root.id)
return
selectAnimation.start()
return;
selectAnimation.start();
}
}
}
......@@ -444,10 +426,10 @@ Control {
width: {
if (root.readers.length === 0)
return 0
var nbAvatars = root.readers.length
var margin = JamiTheme.avatarReadReceiptSize / 3
return nbAvatars * JamiTheme.avatarReadReceiptSize - (nbAvatars - 1) * margin
return 0;
var nbAvatars = root.readers.length;
var margin = JamiTheme.avatarReadReceiptSize / 3;
return nbAvatars * JamiTheme.avatarReadReceiptSize - (nbAvatars - 1) * margin;
}
height: JamiTheme.avatarReadReceiptSize
......@@ -458,20 +440,20 @@ Control {
}
EmojiReactions {
id: emojiReaction
id: emojiReactions
property bool isOutgoing: Author === CurrentAccount.uri
Layout.alignment: isOutgoing ? Qt.AlignRight : Qt.AlignLeft
Layout.rightMargin: isOutgoing ? status.width : undefined
Layout.leftMargin: !isOutgoing ? avatarBlock.width : undefined
Layout.topMargin: - contentHeight/4
Layout.topMargin: -contentHeight / 4
Layout.preferredHeight: contentHeight + 5
Layout.preferredWidth: contentWidth
emojiReaction: Reactions
reactions: Reactions
TapHandler {
onTapped: {
reactionPopup.open()
reactionPopup.open();
}
}
}
......@@ -483,10 +465,10 @@ Control {
orientation: ListView.Horizontal
Layout.preferredHeight: {
if (showTime || seq === MsgSeq.last)
return contentHeight + timestampItem.contentHeight
return contentHeight + timestampItem.contentHeight;
else if (readsMultiple.visible)
return JamiTheme.avatarReadReceiptSize
return 0
return JamiTheme.avatarReadReceiptSize;
return 0;
}
ReadStatus {
......@@ -494,14 +476,14 @@ Control {
visible: root.readers.length > 1 && CurrentAccount.sendReadReceipt
width: {
if (root.readers.length === 0)
return 0
var nbAvatars = root.readers.length
var margin = JamiTheme.avatarReadReceiptSize / 3
return nbAvatars * JamiTheme.avatarReadReceiptSize - (nbAvatars - 1) * margin
return 0;
var nbAvatars = root.readers.length;
var margin = JamiTheme.avatarReadReceiptSize / 3;
return nbAvatars * JamiTheme.avatarReadReceiptSize - (nbAvatars - 1) * margin;
}
anchors.right: parent.right
anchors.top : parent.top
anchors.top: parent.top
anchors.topMargin: 1
readers: root.readers
}
......@@ -511,7 +493,7 @@ Control {
EmojiReactionPopup {
id: reactionPopup
emojiReaction: Reactions
reactions: Reactions
msgId: Id
}
}
......@@ -3,6 +3,7 @@
<file>src/tst_LocalAccount.qml</file>
<file>src/tst_WizardView.qml</file>
<file>src/tst_NewSwarmPage.qml</file>
<file>src/tst_MessageOptions.qml</file>
<file>src/resources/gif_test.gif</file>
<file>src/resources/gz_test.gz</file>
<file>src/resources/png_test.png</file>
......
/*
* Copyright (C) 2023 Savoir-faire Linux Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import QtQuick
import QtTest
import net.jami.Adapters 1.1
import net.jami.Models 1.1
import net.jami.Constants 1.1
import net.jami.Enums 1.1
import "../../../src/app/"
import "../../../src/app/commoncomponents"
Item {
id: uut
// Mock reactions
EmojiReactions {
id: emojiReactions
}
// Mock bubble item
Item {
id: bubble
}
// Mock listview
JamiListView {
id: listView
}
property int id
function getId() {
id += 1;
return "test" + id;
}
function getOptionsPopup(isOutgoing, id, body, type, transferName) {
var component = Qt.createComponent("qrc:/commoncomponents/MessageOptionsPopup.qml");
var obj = component.createObject(bubble, {
"emojiReactions": emojiReactions,
"isOutgoing": isOutgoing,
"msgId": id,
"msgBody": body,
"type": type,
"transferName": transferName,
"msgBubble": bubble,
"listView": listView
});
return obj;
}
SignalSpy {
id: accountAdded
target: AccountAdapter
signalName: "accountAdded"
}
TestCase {
name: "Test message options popup instantiation"
when: windowShown
function test_createMessageOptionsPopup() {
// Create an account and set it as current account
AccountAdapter.createSIPAccount({
"username": "currentAccountUsername"
});
// Block on account creation
accountAdded.wait(1000);
// Add some emoji reactions (one from current account uri, one from another uri)
emojiReactions.reactions = {
"currentAccountUsername": ["🌭"],
"notCurrentAccountUri": ["🌮"]
};
var optionsPopup = getOptionsPopup(true, getId(), "test", 0, "test");
verify(optionsPopup !== null, "Message options popup should be created");
// Check if the popup is visible once opened.
optionsPopup.open();
verify(optionsPopup.visible, "Message options popup should be visible");
// Check that emojiReplied has our emoji.
verify(JSON.stringify(optionsPopup.emojiReplied) === JSON.stringify(["🌭"]),
"Message options popup should have emoji replied");
}
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment