From f8276ac83479fceed5ecc6ff3b68750d2d90141d Mon Sep 17 00:00:00 2001 From: Christophe Deschamps Date: Mon, 19 May 2025 15:50:14 +0000 Subject: [PATCH] Meeting invitation display in chat conversations --- .../core/chat/message/ChatMessageCore.cpp | 12 + .../core/chat/message/ChatMessageCore.hpp | 10 + .../core/conference/ConferenceInfoCore.cpp | 8 +- Linphone/data/languages/en.ts | 69 +++++ Linphone/data/languages/fr_FR.ts | 69 +++++ .../model/chat/message/ChatMessageModel.cpp | 11 + .../model/chat/message/ChatMessageModel.hpp | 3 + .../model/conference/ConferenceInfoModel.cpp | 4 +- Linphone/model/tool/ToolModel.cpp | 11 +- Linphone/view/CMakeLists.txt | 1 + .../view/Control/Display/Chat/ChatMessage.qml | 41 ++- .../Chat/ChatMessageInvitationBubble.qml | 258 ++++++++++++++++++ Linphone/view/Style/Typography.qml | 2 +- 13 files changed, 479 insertions(+), 20 deletions(-) create mode 100644 Linphone/view/Control/Display/Chat/ChatMessageInvitationBubble.qml diff --git a/Linphone/core/chat/message/ChatMessageCore.cpp b/Linphone/core/chat/message/ChatMessageCore.cpp index 93462c7c6..804e127cf 100644 --- a/Linphone/core/chat/message/ChatMessageCore.cpp +++ b/Linphone/core/chat/message/ChatMessageCore.cpp @@ -39,6 +39,8 @@ ChatMessageCore::ChatMessageCore(const std::shared_ptr &c mChatMessageModel = Utils::makeQObject_ptr(chatmessage); mChatMessageModel->setSelf(mChatMessageModel); mText = mChatMessageModel->getText(); + mUtf8Text = mChatMessageModel->getUtf8Text(); + mHasTextContent = mChatMessageModel->getHasTextContent(); mTimestamp = QDateTime::fromSecsSinceEpoch(chatmessage->getTime()); auto from = chatmessage->getFromAddress(); auto to = chatmessage->getLocalAddress(); @@ -56,6 +58,12 @@ ChatMessageCore::ChatMessageCore(const std::shared_ptr &c mIsRead = chatmessage->isRead(); mMessageState = LinphoneEnums::fromLinphone(chatmessage->getState()); mMessageId = Utils::coreStringToAppString(chatmessage->getMessageId()); + for (auto content : chatmessage->getContents()) { + if (content->isIcalendar()) { + auto conferenceInfo = linphone::Factory::get()->createConferenceInfoFromIcalendarContent(content); + mConferenceInfo = ConferenceInfoCore::create(conferenceInfo); + } + } } ChatMessageCore::~ChatMessageCore() { @@ -165,3 +173,7 @@ void ChatMessageCore::setMessageState(LinphoneEnums::ChatMessageState state) { std::shared_ptr ChatMessageCore::getModel() const { return mChatMessageModel; } + +ConferenceInfoGui *ChatMessageCore::getConferenceInfoGui() const { + return mConferenceInfo ? new ConferenceInfoGui(mConferenceInfo) : nullptr; +} diff --git a/Linphone/core/chat/message/ChatMessageCore.hpp b/Linphone/core/chat/message/ChatMessageCore.hpp index 467eb636a..16d3ef5e4 100644 --- a/Linphone/core/chat/message/ChatMessageCore.hpp +++ b/Linphone/core/chat/message/ChatMessageCore.hpp @@ -21,6 +21,8 @@ #ifndef CHATMESSAGECORE_H_ #define CHATMESSAGECORE_H_ +#include "core/conference/ConferenceInfoCore.hpp" +#include "core/conference/ConferenceInfoGui.hpp" #include "model/chat/message/ChatMessageModel.hpp" #include "tool/AbstractObject.hpp" #include "tool/thread/SafeConnection.hpp" @@ -35,6 +37,8 @@ class ChatMessageCore : public QObject, public AbstractObject { Q_OBJECT Q_PROPERTY(QDateTime timestamp READ getTimestamp WRITE setTimestamp NOTIFY timestampChanged) Q_PROPERTY(QString text READ getText WRITE setText NOTIFY textChanged) + Q_PROPERTY(QString utf8Text MEMBER mUtf8Text CONSTANT) + Q_PROPERTY(bool hasTextContent MEMBER mHasTextContent CONSTANT) Q_PROPERTY(QString peerAddress READ getPeerAddress CONSTANT) Q_PROPERTY(QString fromAddress READ getFromAddress CONSTANT) Q_PROPERTY(QString toAddress READ getToAddress CONSTANT) @@ -45,6 +49,7 @@ class ChatMessageCore : public QObject, public AbstractObject { Q_PROPERTY(bool isRemoteMessage READ isRemoteMessage CONSTANT) Q_PROPERTY(bool isFromChatGroup READ isFromChatGroup CONSTANT) Q_PROPERTY(bool isRead READ isRead WRITE setIsRead NOTIFY isReadChanged) + Q_PROPERTY(ConferenceInfoGui *conferenceInfo READ getConferenceInfoGui CONSTANT) public: static QSharedPointer create(const std::shared_ptr &chatmessage); @@ -75,10 +80,12 @@ public: void setMessageState(LinphoneEnums::ChatMessageState state); std::shared_ptr getModel() const; + ConferenceInfoGui *getConferenceInfoGui() const; signals: void timestampChanged(QDateTime timestamp); void textChanged(QString text); + void utf8TextChanged(QString text); void isReadChanged(bool read); void isRemoteMessageChanged(bool isRemote); void messageStateChanged(); @@ -90,6 +97,8 @@ signals: private: DECLARE_ABSTRACT_OBJECT QString mText; + QString mUtf8Text; + bool mHasTextContent; QString mPeerAddress; QString mFromAddress; QString mToAddress; @@ -101,6 +110,7 @@ private: bool mIsFromChatGroup = false; bool mIsRead = false; LinphoneEnums::ChatMessageState mMessageState; + QSharedPointer mConferenceInfo = nullptr; std::shared_ptr mChatMessageModel; QSharedPointer> mChatMessageModelConnection; diff --git a/Linphone/core/conference/ConferenceInfoCore.cpp b/Linphone/core/conference/ConferenceInfoCore.cpp index fb21e70bf..4d1ea0294 100644 --- a/Linphone/core/conference/ConferenceInfoCore.cpp +++ b/Linphone/core/conference/ConferenceInfoCore.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop @@ -74,11 +74,7 @@ ConferenceInfoCore::ConferenceInfoCore(std::shared_ptr mEndDateTime = mDateTime.addSecs(mDuration * 60); mIsScheduled = mDateTime.isValid(); mOrganizerAddress = Utils::coreStringToAppString(conferenceInfo->getOrganizer()->asStringUriOnly()); - mOrganizerName = Utils::coreStringToAppString(conferenceInfo->getOrganizer()->getDisplayName()); - if (mOrganizerName.isEmpty()) { - mOrganizerName = Utils::coreStringToAppString(conferenceInfo->getOrganizer()->getUsername()); - mOrganizerName.replace(".", " "); - } + mOrganizerName = mConferenceInfoModel->getOrganizerName(); mSubject = Utils::coreStringToAppString(conferenceInfo->getSubject()); mDescription = Utils::coreStringToAppString(conferenceInfo->getDescription()); mIsEnded = getDateTimeUtc().addSecs(mDuration * 60) < QDateTime::currentDateTimeUtc(); diff --git a/Linphone/data/languages/en.ts b/Linphone/data/languages/en.ts index 345af992f..87f21d126 100644 --- a/Linphone/data/languages/en.ts +++ b/Linphone/data/languages/en.ts @@ -4316,6 +4316,24 @@ To enable them in a commercial project, please contact us. unknown_audio_device_name Unknown device name + + + conference_invitation + "Invitation à une réunion;" + Meeting invitation + + + + conference_invitation_cancelled + "Annulation d'une réunion;" + Meeting cancellation + + + + conference_invitation_updated + "Modification d'une réunion;" + Meeting modification + Utils @@ -5831,4 +5849,55 @@ Failed to create 1-1 conversation with %1 ! Ok + + ChatMessageInvitationBubble + + + ics_bubble_organiser_invites_you_to + " vous invite à :;" + invites you to : + + + + ics_bubble_organiser_modified + " a modifié :;" + modified : + + + + ics_bubble_organiser_cancelled + " a annulé :;" + cancelled : + + + + ics_bubble_meeting_from + "de ;" + from + + + + ics_bubble_meeting_to + " à ;" + to + + + + ics_bubble_description_title + "Description;" + Description + + + + ics_bubble_join + "Rejoindre;" + Join + + + + ics_bubble_participants + " participants;" + participants + + diff --git a/Linphone/data/languages/fr_FR.ts b/Linphone/data/languages/fr_FR.ts index 61c13bf78..ffee3615d 100644 --- a/Linphone/data/languages/fr_FR.ts +++ b/Linphone/data/languages/fr_FR.ts @@ -4331,6 +4331,24 @@ Pour les activer dans un projet commercial, merci de nous contacter.unknown_audio_device_name Appareil inconnu + + + conference_invitation + "Invitation à une réunion;" + Invitation à une réunion + + + + conference_invitation_cancelled + "Annulation d'une réunion;" + Annulation d'une réunion + + + + conference_invitation_updated + "Modification d'une réunion;" + Modification d'une réunion + Utils @@ -5846,4 +5864,55 @@ Failed to create 1-1 conversation with %1 ! Ok + + ChatMessageInvitationBubble + + + ics_bubble_organiser_invites_you_to + " vous invite à :" + vous invite à : + + + + ics_bubble_organiser_modified + " a modifié :;" + a modifié : + + + + ics_bubble_organiser_cancelled + " a annulé :;" + a annulé : + + + + ics_bubble_meeting_from + "de ;" + de + + + + ics_bubble_meeting_to + " à ;" + à + + + + ics_bubble_description_title + "Description;" + Description + + + + ics_bubble_join + "Rejoindre;" + Rejoindre + + + + ics_bubble_participants + " participants;" + participants + + diff --git a/Linphone/model/chat/message/ChatMessageModel.cpp b/Linphone/model/chat/message/ChatMessageModel.cpp index 4f987bbdd..c6457ea9e 100644 --- a/Linphone/model/chat/message/ChatMessageModel.cpp +++ b/Linphone/model/chat/message/ChatMessageModel.cpp @@ -44,6 +44,17 @@ QString ChatMessageModel::getText() const { return ToolModel::getMessageFromContent(mMonitor->getContents()); } +QString ChatMessageModel::getUtf8Text() const { + return Utils::coreStringToAppString(mMonitor->getUtf8Text()); +} + +bool ChatMessageModel::getHasTextContent() const { + for (auto content : mMonitor->getContents()) { + if (content->isText()) return true; + } + return false; +} + QString ChatMessageModel::getPeerAddress() const { return Utils::coreStringToAppString(mMonitor->getPeerAddress()->asStringUriOnly()); } diff --git a/Linphone/model/chat/message/ChatMessageModel.hpp b/Linphone/model/chat/message/ChatMessageModel.hpp index 3c5cebb89..0019d7c5b 100644 --- a/Linphone/model/chat/message/ChatMessageModel.hpp +++ b/Linphone/model/chat/message/ChatMessageModel.hpp @@ -38,6 +38,9 @@ public: ~ChatMessageModel(); QString getText() const; + QString getUtf8Text() const; + bool getHasTextContent() const; + QDateTime getTimestamp() const; QString getPeerAddress() const; diff --git a/Linphone/model/conference/ConferenceInfoModel.cpp b/Linphone/model/conference/ConferenceInfoModel.cpp index a8bbb92de..12c98d1cc 100644 --- a/Linphone/model/conference/ConferenceInfoModel.cpp +++ b/Linphone/model/conference/ConferenceInfoModel.cpp @@ -111,9 +111,7 @@ linphone::ConferenceInfo::State ConferenceInfoModel::getState() const { QString ConferenceInfoModel::getOrganizerName() const { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); auto organizer = mConferenceInfo->getOrganizer()->clone(); - auto name = Utils::coreStringToAppString(organizer->getDisplayName()); - if (name.isEmpty()) name = ToolModel::getDisplayName(organizer); - return name; + return ToolModel::getDisplayName(organizer); } QString ConferenceInfoModel::getOrganizerAddress() const { diff --git a/Linphone/model/tool/ToolModel.cpp b/Linphone/model/tool/ToolModel.cpp index da0069e90..abd1ada1b 100644 --- a/Linphone/model/tool/ToolModel.cpp +++ b/Linphone/model/tool/ToolModel.cpp @@ -28,6 +28,7 @@ #include #include #include +#include "core/conference/ConferenceInfoCore.hpp" DEFINE_ABSTRACT_OBJECT(ToolModel) @@ -385,13 +386,21 @@ bool ToolModel::friendIsInFriendList(const std::shared_ptr } QString ToolModel::getMessageFromContent(std::list> contents) { + mustBeInLinphoneThread(sLog().arg(Q_FUNC_INFO)); for (auto &content : contents) { if (content->isText()) { return Utils::coreStringToAppString(content->getUtf8Text()); } else if (content->isFile()) { return Utils::coreStringToAppString(content->getName()); } else if (content->isIcalendar()) { - return QString("Invitation à une réunion"); + auto conferenceInfo = linphone::Factory::get()->createConferenceInfoFromIcalendarContent(content); + auto conferenceInfoCore = ConferenceInfoCore::create(conferenceInfo); + if (conferenceInfoCore->getConferenceInfoState() == LinphoneEnums::ConferenceInfoState::New) + return tr("conference_invitation"); + if (conferenceInfoCore->getConferenceInfoState() == LinphoneEnums::ConferenceInfoState::Updated) + return tr("conference_invitation_updated"); + if (conferenceInfoCore->getConferenceInfoState() == LinphoneEnums::ConferenceInfoState::Cancelled) + return tr("conference_invitation_cancelled"); } else if (content->isMultipart()) { return getMessageFromContent(content->getParts()); } diff --git a/Linphone/view/CMakeLists.txt b/Linphone/view/CMakeLists.txt index 1e30cdec6..b3de78c28 100644 --- a/Linphone/view/CMakeLists.txt +++ b/Linphone/view/CMakeLists.txt @@ -54,6 +54,7 @@ list(APPEND _LINPHONEAPP_QML_FILES view/Control/Display/Call/CallStatistics.qml view/Control/Display/Chat/ChatListView.qml view/Control/Display/Chat/ChatMessage.qml + view/Control/Display/Chat/ChatMessageInvitationBubble.qml view/Control/Display/Chat/ChatMessagesListView.qml view/Control/Display/Contact/Avatar.qml view/Control/Display/Contact/Contact.qml diff --git a/Linphone/view/Control/Display/Chat/ChatMessage.qml b/Linphone/view/Control/Display/Chat/ChatMessage.qml index e3d0d9a98..fb06b2860 100644 --- a/Linphone/view/Control/Display/Chat/ChatMessage.qml +++ b/Linphone/view/Control/Display/Chat/ChatMessage.qml @@ -20,7 +20,7 @@ Control.Control { property bool isRemoteMessage: chatMessage? chatMessage.core.isRemoteMessage : false property bool isFromChatGroup: chatMessage? chatMessage.core.isFromChatGroup : false property var msgState: chatMessage ? chatMessage.core.messageState : LinphoneEnums.ChatMessageState.StateIdle - property string richFormatText: UtilsCpp.encodeTextToQmlRichFormat(modelData.core.text) + property string richFormatText: modelData.core.hasTextContent ? UtilsCpp.encodeTextToQmlRichFormat(modelData.core.utf8Text) : "" hoverEnabled: true property bool linkHovered: false @@ -43,6 +43,12 @@ Control.Control { } } } + + function handleDefaultMouseEvent(event) { + if (event.button === Qt.RightButton) { + optionsMenu.open() + } + } contentItem: RowLayout { spacing: 0 @@ -69,15 +75,13 @@ Control.Control { leftPadding: Math.round(12 * DefaultStyle.dp) rightPadding: Math.round(12 * DefaultStyle.dp) - MouseArea { + MouseArea { // Default mouse area. Each sub bubble can control the mouse and pass on to the main mouse handler. Child bubble mouse area must cover the entire bubble. + id: defaultMouseArea + visible: invitationLoader.status !== Loader.Ready // Add other bubbles here that could control the mouse themselves, then add in bubble a signal onMouseEvent anchors.fill: parent acceptedButtons: Qt.RightButton - onClicked: (mouse) => { - if (mouse.button === Qt.RightButton) { - optionsMenu.open() - } - } - cursorShape: mainItem.linkHovered ? Qt.PointingHandCursor : Qt.IBeamCursor + onClicked: (mouse) => mainItem.handleDefaultMouseEvent(mouse) + cursorShape: mainItem.linkHovered ? Qt.PointingHandCursor : Qt.ArrowCursor } background: Item { @@ -110,7 +114,7 @@ Control.Control { visible: mainItem.imgUrl != undefined id: contentimage } - Text { + Text { // Uses default mouse area for link hovering. id: textElement visible: mainItem.richFormatText !== "" text: mainItem.richFormatText @@ -134,6 +138,25 @@ Control.Control { mainItem.linkHovered = hoveredLink !== "" } } + + // Meeting invitation bubble + ///////////////////////////// + Loader { + id: invitationLoader + active: modelData.core.conferenceInfo !== null + sourceComponent: invitationComponent + } + Component { + id: invitationComponent + ChatMessageInvitationBubble { + conferenceInfoGui: modelData.core.conferenceInfo + Layout.fillWidth: true + Layout.fillHeight: true + onMouseEvent: mainItem.handleDefaultMouseEvent(event) + } + } + ///////////////////////////// + RowLayout { Layout.alignment: Qt.AlignRight Text { diff --git a/Linphone/view/Control/Display/Chat/ChatMessageInvitationBubble.qml b/Linphone/view/Control/Display/Chat/ChatMessageInvitationBubble.qml new file mode 100644 index 000000000..4e5f3997d --- /dev/null +++ b/Linphone/view/Control/Display/Chat/ChatMessageInvitationBubble.qml @@ -0,0 +1,258 @@ +import QtQuick +import QtQuick.Effects +import QtQuick.Layouts +import QtQuick.Controls.Basic as Control +import Linphone +import UtilsCpp +import SettingsCpp +import "qrc:/qt/qml/Linphone/view/Style/buttonStyle.js" as ButtonStyle +import "qrc:/qt/qml/Linphone/view/Control/Tool/Helper/utils.js" as Utils + +Rectangle { + id: mainItem + width: 490 * DefaultStyle.dp + height: content.implicitHeight + radius: 10 * DefaultStyle.dp + clip: true + antialiasing: true + + property var conferenceInfoGui: ConferenceInfoGui + property var conferenceInfo: conferenceInfoGui?.core + property string timeRangeText: "" + property bool linkHovered: false + + signal mouseEvent(MouseEvent event) + + MouseArea { + anchors.fill: parent + cursorShape: mainItem.linkHovered ? Qt.PointingHandCursor : Qt.ArrowCursor + onClicked: (mouse) => mouseEvent(mouse) + acceptedButtons: Qt.AllButtons // Send all to parent + } + + function updateTimeRange() { + if (!conferenceInfo || !conferenceInfo.dateTime || !conferenceInfo.duration) + return; + + let locale = Qt.locale(); + let startDate = conferenceInfo.dateTime; + let endDate = new Date(startDate.getTime() + (conferenceInfo.duration * 60 * 1000)); + + let startTime = startDate.toLocaleTimeString(locale, "hh:mm"); + let endTime = endDate.toLocaleTimeString(locale, "hh:mm"); + + let offsetMinutes = -startDate.getTimezoneOffset(); + let offsetHours = Math.floor(offsetMinutes / 60); + let timeZone = "UTC" + (offsetHours >= 0 ? "+" : "") + offsetHours; + + timeRangeText = + qsTr("ics_bubble_meeting_from") + startTime + + qsTr("ics_bubble_meeting_to") + endTime + " (" + timeZone + ")"; + } + + ColumnLayout { + id: content + anchors.fill: parent + spacing: 0 + + Rectangle { + Layout.fillWidth: true + height: row1.implicitHeight + 32 * DefaultStyle.dp + color: DefaultStyle.grey_100 + radius: 10 * DefaultStyle.dp // rounded all, but only top visible + + ColumnLayout { + id: row1 + anchors.fill: parent + anchors.margins: 16 * DefaultStyle.dp + + Text { + text: conferenceInfo.organizerName + ( + conferenceInfo.state == LinphoneEnums.ConferenceInfoState.New ? + qsTr("ics_bubble_organiser_invites_you_to") : + conferenceInfo.state == LinphoneEnums.ConferenceInfoState.Updated ? + qsTr("ics_bubble_organiser_modified") : + conferenceInfo.state == LinphoneEnums.ConferenceInfoState.Cancelled ? + qsTr("ics_bubble_organiser_cancelled") : + "" + ) + font: Typography.p2 + color: conferenceInfo.state == LinphoneEnums.ConferenceInfoState.New ? + DefaultStyle.main2_600 : + conferenceInfo.state == LinphoneEnums.ConferenceInfoState.Updated ? + DefaultStyle.warning_600 : + conferenceInfo.state == LinphoneEnums.ConferenceInfoState.Cancelled ? + DefaultStyle.danger_500main : + DefaultStyle.main2_600 + } + + RowLayout { + Layout.fillWidth: true + spacing: 10 * DefaultStyle.dp + + Rectangle { + width: 48 * DefaultStyle.dp + height: 48 * DefaultStyle.dp + radius: 10 * DefaultStyle.dp + color: "transparent" + + Rectangle { + anchors.fill: parent + color: "#33000000" + radius: 10 * DefaultStyle.dp + anchors.verticalCenterOffset: 4 * DefaultStyle.dp + z: -1 + } + + Rectangle { + id: dayRect + anchors.fill: parent + radius: 10 * DefaultStyle.dp + color: DefaultStyle.grey_0 + + Column { + anchors.centerIn: parent + spacing: 2 * DefaultStyle.dp + + Text { + text: conferenceInfo.dateTime.toLocaleString(Qt.locale(), "ddd") + "." + color: DefaultStyle.main2_500main + font: Typography.p4 + horizontalAlignment: Text.AlignHCenter + anchors.horizontalCenter: parent.horizontalCenter + } + + Rectangle { + width: 23 * DefaultStyle.dp + height: 23 * DefaultStyle.dp + radius: width / 2 + color: DefaultStyle.main1_500_main + anchors.horizontalCenter: parent.horizontalCenter + + Text { + text: conferenceInfo.dateTime.getDate().toString() + color: DefaultStyle.grey_0 + font: Typography.h4 + anchors.centerIn: parent + } + } + } + } + } + + // Info + ColumnLayout { + RowLayout { + EffectImage { + imageSource: AppIcons.usersThree + colorizationColor: DefaultStyle.main2_600 + Layout.preferredWidth: Math.round(24 * DefaultStyle.dp) + Layout.preferredHeight: Math.round(24 * DefaultStyle.dp) + } + + Text { + text: conferenceInfo.subject + font: Typography.p2 + wrapMode: Text.WordWrap + color: DefaultStyle.main2_600 + } + } + + Text { + text: conferenceInfo.dateTime.toLocaleString(Qt.locale(), "dddd d MMMM yyyy") + font: Typography.p4 + color: DefaultStyle.main2_500main + } + + Text { + text: timeRangeText + font: Typography.p4 + color: DefaultStyle.main2_500main + } + } + } + } + } + + + Rectangle { + Layout.fillWidth: true + height: 10 * DefaultStyle.dp + color: DefaultStyle.grey_100 + Layout.topMargin: -10 * DefaultStyle.dp + } + + Rectangle { + Layout.fillWidth: true + height: row2.implicitHeight + 32 * DefaultStyle.dp + color: DefaultStyle.grey_0 + radius: 10 * DefaultStyle.dp + + ColumnLayout { + id: row2 + spacing: 10 * DefaultStyle.dp + anchors.fill: parent + anchors.margins: 16 * DefaultStyle.dp + + Text { + text: qsTr("ics_bubble_description_title") + font: Typography.p4 + color: DefaultStyle.main2_800 + visible: conferenceInfo.description.length > 0 + } + + Text { + text: UtilsCpp.encodeTextToQmlRichFormat(conferenceInfo.description) + wrapMode: Text.WordWrap + textFormat: Text.RichText + font: Typography.p4 + color: DefaultStyle.main2_500main + visible: conferenceInfo.description.length > 0 + onLinkActivated: { + if (link.startsWith('sip')) + UtilsCpp.createCall(link) + else + Qt.openUrlExternally(link) + } + onHoveredLinkChanged: { + mainItem.linkHovered = hoveredLink !== "" + } + } + + RowLayout { + Layout.fillHeight: true + Layout.preferredHeight: 30 * DefaultStyle.dp + spacing: 10 * DefaultStyle.dp + EffectImage { + imageSource: AppIcons.usersTwo + colorizationColor: DefaultStyle.main2_600 + Layout.preferredWidth: Math.round(14 * DefaultStyle.dp) + Layout.preferredHeight: Math.round(14 * DefaultStyle.dp) + } + Text { + text: conferenceInfo.participantCount + qsTr("ics_bubble_participants") + font: Typography.p4 + color: DefaultStyle.main2_800 + } + Item { + Layout.fillWidth: true + } + MediumButton { + style: ButtonStyle.ButtonStyle + //: "Rejoindre" + text: qsTr("ics_bubble_join") + visible: !SettingsCpp.disableMeetingsFeature && conferenceInfo.state != LinphoneEnums.ConferenceInfoState.Cancelled + onClicked: { + var callsWindow = UtilsCpp.getCallsWindow() + callsWindow.setupConference(mainItem.conferenceInfoGui) + UtilsCpp.smartShowWindow(callsWindow) + } + } + } + Item { + Layout.fillHeight: true + } + } + } + } +} diff --git a/Linphone/view/Style/Typography.qml b/Linphone/view/Style/Typography.qml index 3c215ada9..884d8fce9 100644 --- a/Linphone/view/Style/Typography.qml +++ b/Linphone/view/Style/Typography.qml @@ -23,7 +23,7 @@ QtObject { pixelSize: Math.round(20 * DefaultStyle.dp), weight: Math.min(Math.round(800 * DefaultStyle.dp), 1000) }) - + // Title/H2 - Large bloc title property font h2: Qt.font( { family: DefaultStyle.defaultFont,