From 4bb1e5da43b180c6eb9a7c0103fbe060bbb4369e Mon Sep 17 00:00:00 2001 From: Gaelle Braud Date: Tue, 13 May 2025 15:46:36 +0200 Subject: [PATCH] open chat when clicking on message notification select chat by clicking on notification close all notifications when one clicked and chat is open --- Linphone/core/chat/ChatCore.cpp | 33 ++++++++++++------- Linphone/core/chat/ChatCore.hpp | 16 ++++++--- Linphone/core/chat/ChatProxy.cpp | 2 +- Linphone/core/notifier/Notifier.cpp | 7 ++-- Linphone/tool/Utils.cpp | 10 ++++++ Linphone/tool/Utils.hpp | 1 + .../Control/Display/Chat/ChatListView.qml | 4 +-- .../Display/Chat/ChatMessagesListView.qml | 7 ++++ .../NotificationReceivedMessage.qml | 17 ++++++++-- .../view/Page/Form/Chat/SelectedChatView.qml | 7 ++-- Linphone/view/Page/Layout/Main/MainLayout.qml | 9 +++++ Linphone/view/Page/Window/Main/MainWindow.qml | 4 +++ 12 files changed, 90 insertions(+), 27 deletions(-) diff --git a/Linphone/core/chat/ChatCore.cpp b/Linphone/core/chat/ChatCore.cpp index 6bfc12d50..0b8ccecd7 100644 --- a/Linphone/core/chat/ChatCore.cpp +++ b/Linphone/core/chat/ChatCore.cpp @@ -41,11 +41,14 @@ ChatCore::ChatCore(const std::shared_ptr &chatRoom) : QObjec mustBeInLinphoneThread(getClassName()); App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::CppOwnership); mLastUpdatedTime = QDateTime::fromSecsSinceEpoch(chatRoom->getLastUpdateTime()); + auto chatRoomAddress = chatRoom->getPeerAddress()->clone(); + chatRoomAddress->clean(); + mChatRoomAddress = Utils::coreStringToAppString(chatRoomAddress->asStringUriOnly()); if (chatRoom->hasCapability((int)linphone::ChatRoom::Capabilities::Basic)) { - mTitle = ToolModel::getDisplayName(chatRoom->getPeerAddress()->clone()); - mAvatarUri = ToolModel::getDisplayName(chatRoom->getPeerAddress()->clone()); - auto peerAddress = chatRoom->getPeerAddress(); - mPeerAddress = Utils::coreStringToAppString(peerAddress->asStringUriOnly()); + mTitle = ToolModel::getDisplayName(chatRoomAddress); + mAvatarUri = ToolModel::getDisplayName(chatRoomAddress); + mPeerAddress = Utils::coreStringToAppString(chatRoomAddress->asStringUriOnly()); + mIsGroupChat = false; } else { if (chatRoom->hasCapability((int)linphone::ChatRoom::Capabilities::OneToOne)) { auto participants = chatRoom->getParticipants(); @@ -58,9 +61,11 @@ ChatCore::ChatCore(const std::shared_ptr &chatRoom) : QObjec if (peerAddress) mPeerAddress = Utils::coreStringToAppString(peerAddress->asStringUriOnly()); } } + mIsGroupChat = false; } else if (chatRoom->hasCapability((int)linphone::ChatRoom::Capabilities::Conference)) { mTitle = Utils::coreStringToAppString(chatRoom->getSubject()); mAvatarUri = Utils::coreStringToAppString(chatRoom->getSubject()); + mIsGroupChat = true; } } mUnreadMessagesCount = chatRoom->getUnreadMessagesCount(); @@ -101,7 +106,10 @@ void ChatCore::setSelf(QSharedPointer me) { mChatModelConnection->makeConnectToModel(&ChatModel::historyDeleted, [this]() { mChatModelConnection->invokeToCore([this]() { clearMessagesList(); - Utils::showInformationPopup(tr("Supprimé"), tr("L'historique des messages a été supprimé."), true); + //: Deleted + Utils::showInformationPopup(tr("info_toast_deleted_title"), + //: Message history has been deleted + tr("info_toast_deleted_message_history"), true); }); }); mChatModelConnection->makeConnectToCore(&ChatCore::lUpdateUnreadCount, [this]() { @@ -170,7 +178,7 @@ void ChatCore::setSelf(QSharedPointer me) { auto lastMessageModel = mLastMessage ? mLastMessage->getModel() : nullptr; mChatModelConnection->invokeToModel([this, lastMessageModel]() { auto linphoneMessage = mChatModel->getLastChatMessage(); - if (lastMessageModel && lastMessageModel->getMonitor() != linphoneMessage) { + if (!lastMessageModel || lastMessageModel->getMonitor() != linphoneMessage) { auto chatMessageCore = ChatMessageCore::create(linphoneMessage); mChatModelConnection->invokeToCore([this, chatMessageCore]() { setLastMessage(chatMessageCore); }); } @@ -232,6 +240,10 @@ void ChatCore::setTitle(QString title) { } } +bool ChatCore::isGroupChat() const { + return mIsGroupChat; +} + QString ChatCore::getIdentifier() const { return mIdentifier; } @@ -240,11 +252,8 @@ QString ChatCore::getPeerAddress() const { return mPeerAddress; } -void ChatCore::setPeerAddress(QString peerAddress) { - if (mPeerAddress != peerAddress) { - mPeerAddress = peerAddress; - emit peerAddressChanged(peerAddress); - } +QString ChatCore::getChatRoomAddress() const { + return mChatRoomAddress; } QString ChatCore::getAvatarUri() const { @@ -359,4 +368,4 @@ QString ChatCore::getComposingAddress() const { std::shared_ptr ChatCore::getModel() const { return mChatModel; -} \ No newline at end of file +} diff --git a/Linphone/core/chat/ChatCore.hpp b/Linphone/core/chat/ChatCore.hpp index ccd6f029b..8a6b22fda 100644 --- a/Linphone/core/chat/ChatCore.hpp +++ b/Linphone/core/chat/ChatCore.hpp @@ -36,7 +36,8 @@ class ChatCore : public QObject, public AbstractObject { public: Q_PROPERTY(QString title READ getTitle WRITE setTitle NOTIFY titleChanged) Q_PROPERTY(QString identifier READ getIdentifier CONSTANT) - Q_PROPERTY(QString peerAddress READ getPeerAddress WRITE setPeerAddress NOTIFY peerAddressChanged) + Q_PROPERTY(QString peerAddress READ getPeerAddress CONSTANT) + Q_PROPERTY(QString chatRoomAddress READ getChatRoomAddress CONSTANT) Q_PROPERTY(QString avatarUri READ getAvatarUri WRITE setAvatarUri NOTIFY avatarUriChanged) Q_PROPERTY(QDateTime lastUpdatedTime READ getLastUpdatedTime WRITE setLastUpdatedTime NOTIFY lastUpdatedTimeChanged) Q_PROPERTY(QString lastMessageText READ getLastMessageText NOTIFY lastMessageChanged) @@ -46,7 +47,7 @@ public: unreadMessagesCountChanged) Q_PROPERTY(QString composingName READ getComposingName WRITE setComposingName NOTIFY composingUserChanged) Q_PROPERTY(QString composingAddress READ getComposingAddress WRITE setComposingAddress NOTIFY composingUserChanged) - // Q_PROPERTY(VideoStats videoStats READ getVideoStats WRITE setVideoStats NOTIFY videoStatsChanged) + Q_PROPERTY(bool isGroupChat READ isGroupChat CONSTANT) // Should be call from model Thread. Will be automatically in App thread after initialization static QSharedPointer create(const std::shared_ptr &chatRoom); @@ -60,6 +61,8 @@ public: QString getTitle() const; void setTitle(QString title); + bool isGroupChat() const; + QString getIdentifier() const; ChatMessageGui *getLastMessage() const; @@ -73,8 +76,8 @@ public: int getUnreadMessagesCount() const; void setUnreadMessagesCount(int count); + QString getChatRoomAddress() const; QString getPeerAddress() const; - void setPeerAddress(QString peerAddress); QList> getChatMessageList() const; void resetChatMessageList(QList> list); @@ -93,11 +96,14 @@ public: std::shared_ptr getModel() const; +Q_SIGNALS: + // used to close all the notifications when one is clicked + void messageOpen(); + signals: void lastUpdatedTimeChanged(QDateTime time); void lastMessageChanged(); void titleChanged(QString title); - void peerAddressChanged(QString address); void unreadMessagesCountChanged(int count); void messageListChanged(); void messagesInserted(QList> list); @@ -120,12 +126,14 @@ private: QString id; QDateTime mLastUpdatedTime; QString mPeerAddress; + QString mChatRoomAddress; QString mTitle; QString mIdentifier; QString mAvatarUri; int mUnreadMessagesCount; QString mComposingName; QString mComposingAddress; + bool mIsGroupChat = false; std::shared_ptr mChatModel; QSharedPointer mLastMessage; QList> mChatMessageList; diff --git a/Linphone/core/chat/ChatProxy.cpp b/Linphone/core/chat/ChatProxy.cpp index a30a58bc1..98eccf024 100644 --- a/Linphone/core/chat/ChatProxy.cpp +++ b/Linphone/core/chat/ChatProxy.cpp @@ -44,7 +44,7 @@ void ChatProxy::setSourceModel(QAbstractItemModel *model) { connect(this, &ChatProxy::filterTextChanged, newChatList, [this, newChatList] { emit newChatList->filterChanged(getFilterText()); }); connect(newChatList, &ChatList::chatRemoved, this, &ChatProxy::chatRemoved); - // connect(newChatList, &ChatList::chatAdded, this, [this] { invalidate(); }); + connect(newChatList, &ChatList::chatAdded, this, [this] { invalidate(); }); } auto firstList = new SortFilterList(model, Qt::AscendingOrder); firstList->setDynamicSortFilter(true); diff --git a/Linphone/core/notifier/Notifier.cpp b/Linphone/core/notifier/Notifier.cpp index 28eec67bb..fb5f48532 100644 --- a/Linphone/core/notifier/Notifier.cpp +++ b/Linphone/core/notifier/Notifier.cpp @@ -33,7 +33,7 @@ #include "core/App.hpp" #include "core/call/CallGui.hpp" -#include "core/chat/ChatCore.hpp" +#include "core/chat/ChatGui.hpp" #include "model/tool/ToolModel.hpp" #include "tool/LinphoneEnums.hpp" #include "tool/providers/AvatarProvider.hpp" @@ -367,11 +367,12 @@ void Notifier::notifyReceivedMessages(const std::shared_ptr mustBeInMainThread(getClassName()); QVariantMap map; map["message"] = txt; - qDebug() << "create notif from address" << remoteAddress; map["remoteAddress"] = remoteAddress; map["chatRoomName"] = chatCore->getTitle(); - map["chatRoomAddress"] = chatCore->getPeerAddress(); + map["chatRoomAddress"] = chatCore->getChatRoomAddress(); map["avatarUri"] = chatCore->getAvatarUri(); + map["isGroupChat"] = chatCore->isGroupChat(); + map["chat"] = QVariant::fromValue(chatCore ? new ChatGui(chatCore) : nullptr); CREATE_NOTIFICATION(Notifier::ReceivedMessage, map) }); } diff --git a/Linphone/tool/Utils.cpp b/Linphone/tool/Utils.cpp index b9044018f..801a4aea1 100644 --- a/Linphone/tool/Utils.cpp +++ b/Linphone/tool/Utils.cpp @@ -1538,6 +1538,7 @@ VariantObject *Utils::getCurrentCallChat(CallGui *call) { //: Failed to create 1-1 conversation with %1 ! data->mConnection->invokeToCore([] { showInformationPopup(tr("information_popup_error_title"), + //: Failed to create 1-1 conversation with %1 ! tr("information_popup_chatroom_creation_error_message"), false, getCallsWindow()); }); @@ -1583,6 +1584,15 @@ VariantObject *Utils::getChatForAddress(QString address) { return data; } +void Utils::openChat(ChatGui *chat) { + auto mainWindow = getMainWindow(); + smartShowWindow(mainWindow); + if (mainWindow && chat) { + emit chat->mCore->messageOpen(); + QMetaObject::invokeMethod(mainWindow, "openChat", Q_ARG(QVariant, QVariant::fromValue(chat))); + } +} + bool Utils::isEmptyMessage(QString message) { return message.trimmed().isEmpty(); } diff --git a/Linphone/tool/Utils.hpp b/Linphone/tool/Utils.hpp index e799aa78b..c0b526596 100644 --- a/Linphone/tool/Utils.hpp +++ b/Linphone/tool/Utils.hpp @@ -148,6 +148,7 @@ public: Q_INVOKABLE static VariantObject *getCurrentCallChat(CallGui *call); Q_INVOKABLE static VariantObject *getChatForAddress(QString address); + Q_INVOKABLE static void openChat(ChatGui *chat); Q_INVOKABLE static bool isEmptyMessage(QString message); Q_INVOKABLE static QString encodeTextToQmlRichFormat(const QString &text, const QVariantMap &options = QVariantMap()); diff --git a/Linphone/view/Control/Display/Chat/ChatListView.qml b/Linphone/view/Control/Display/Chat/ChatListView.qml index c3000955d..169938dfd 100644 --- a/Linphone/view/Control/Display/Chat/ChatListView.qml +++ b/Linphone/view/Control/Display/Chat/ChatListView.qml @@ -82,8 +82,8 @@ ListView { // Update position only if we are moving to current item and its position is changing. property var _currentItemY: currentItem?.y on_CurrentItemYChanged: if (_currentItemY && moveAnimation.running) { - moveToCurrentItem() - } + moveToCurrentItem() + } Behavior on contentY { NumberAnimation { id: moveAnimation diff --git a/Linphone/view/Control/Display/Chat/ChatMessagesListView.qml b/Linphone/view/Control/Display/Chat/ChatMessagesListView.qml index 47f7594f3..b5a791e80 100644 --- a/Linphone/view/Control/Display/Chat/ChatMessagesListView.qml +++ b/Linphone/view/Control/Display/Chat/ChatMessagesListView.qml @@ -14,6 +14,12 @@ ListView { property color backgroundColor spacing: Math.round(4 * DefaultStyle.dp) + onChatChanged: { + var index = chatMessageProxy.findFirstUnreadIndex() + positionViewAtIndex(index, ListView.End) + } + + Component.onCompleted: { var index = chatMessageProxy.findFirstUnreadIndex() positionViewAtIndex(index, ListView.End) @@ -60,6 +66,7 @@ ListView { chatMessage: modelData property real maxWidth: Math.round(mainItem.width * (3/4)) // height: childrenRect.height + onVisibleChanged: if (!modelData.core.isRead) modelData.core.lMarkAsRead() width: mainItem.width property var previousIndex: index - 1 property var previousFromAddress: chatMessageProxy.getChatMessageAtIndex(index-1)?.core.fromAddress diff --git a/Linphone/view/Control/Popup/Notification/NotificationReceivedMessage.qml b/Linphone/view/Control/Popup/Notification/NotificationReceivedMessage.qml index 8444fba2d..5d7ca4c65 100644 --- a/Linphone/view/Control/Popup/Notification/NotificationReceivedMessage.qml +++ b/Linphone/view/Control/Popup/Notification/NotificationReceivedMessage.qml @@ -14,11 +14,22 @@ Notification { backgroundOpacity: 0.8 overriddenWidth: Math.round(400 * DefaultStyle.dp) overriddenHeight: content.height + + property var chat: notificationData ? notificationData.chat : null property string avatarUri: notificationData ? notificationData.avatarUri : "" property string chatRoomName: notificationData ? notificationData.chatRoomName : "" property string remoteAddress: notificationData ? notificationData.remoteAddress : "" + property string chatRoomAddress: notificationData ? notificationData.chatRoomAddress : "" + property bool isGroupChat: notificationData ? notificationData.isGroupChat : false property string message: notificationData ? notificationData.message : "" + Connections { + enabled: chat + target: chat.core + function onMessageOpen() { + close() + } + } Popup { id: content @@ -65,14 +76,15 @@ Notification { icon.height: Math.round(14 * DefaultStyle.dp) contentImageColor: DefaultStyle.grey_0 onPressed: { - mainItem._close() + mainItem.close() } } MouseArea { id: mousearea anchors.fill: parent onClicked: { - console.log("notif clicked, open chat") + UtilsCpp.openChat(mainItem.chat) + mainItem.close() } } } @@ -102,6 +114,7 @@ Notification { } } Text { + visible: mainItem.isGroupChat text: mainItem.remoteAddress color: DefaultStyle.main2_100 Layout.fillWidth: true diff --git a/Linphone/view/Page/Form/Chat/SelectedChatView.qml b/Linphone/view/Page/Form/Chat/SelectedChatView.qml index 266b38103..087be7c87 100644 --- a/Linphone/view/Page/Form/Chat/SelectedChatView.qml +++ b/Linphone/view/Page/Form/Chat/SelectedChatView.qml @@ -79,6 +79,7 @@ RowLayout { content: [ ChatMessagesListView { id: chatMessagesListView + clip: true height: contentHeight backgroundColor: splitPanel.panelColor width: parent.width - anchors.leftMargin - anchors.rightMargin @@ -86,7 +87,7 @@ RowLayout { anchors.top: parent.top anchors.left: parent.left anchors.right: parent.right - anchors.bottom: messageSender.top + anchors.bottom: messageSender.top anchors.leftMargin: Math.round(18 * DefaultStyle.dp) anchors.rightMargin: Math.round(18 * DefaultStyle.dp) Control.ScrollBar.vertical: scrollbar @@ -95,7 +96,7 @@ RowLayout { id: scrollbar visible: chatMessagesListView.contentHeight > parent.height active: visible - anchors.top: parent.top + anchors.top: chatMessagesListView.top anchors.bottom: chatMessagesListView.bottom anchors.right: parent.right anchors.rightMargin: Math.round(5 * DefaultStyle.dp) @@ -214,7 +215,7 @@ RowLayout { Keys.onPressed: (event) => { event.accepted = false if (UtilsCpp.isEmptyMessage(sendingTextArea.text)) return - if (!(event.modifiers & Qt.ControlModifier) && (event.key == Qt.Key_Return || event.key == Qt.Key_Enter)) { + if (!(event.modifiers & Qt.ShiftModifier) && (event.key == Qt.Key_Return || event.key == Qt.Key_Enter)) { mainItem.chat.core.lSendTextMessage(sendingTextArea.text) sendingTextArea.clear() event.accepted = true diff --git a/Linphone/view/Page/Layout/Main/MainLayout.qml b/Linphone/view/Page/Layout/Main/MainLayout.qml index ab5a3e863..c75474158 100644 --- a/Linphone/view/Page/Layout/Main/MainLayout.qml +++ b/Linphone/view/Page/Layout/Main/MainLayout.qml @@ -27,6 +27,7 @@ Item { signal openNumPadRequest signal displayContactRequested(string contactAddress) signal displayChatRequested(string contactAddress) + signal openChatRequested(ChatGui chat) signal createContactRequested(string name, string address) signal accountRemoved @@ -46,6 +47,10 @@ Item { tabbar.currentIndex = 2 mainItem.displayChatRequested(contactAddress) } + function openChat(chat) { + tabbar.currentIndex = 2 + mainItem.openChatRequested(chat) + } function createContact(name, address) { tabbar.currentIndex = 1 @@ -636,6 +641,10 @@ Item { chatPage.remoteAddress = "" chatPage.remoteAddress = contactAddress } + function onOpenChatRequested(chat) { + console.log("open chat requested, open", chat.core.title) + chatPage.selectedChatGui = chat + } } } MeetingPage {} diff --git a/Linphone/view/Page/Window/Main/MainWindow.qml b/Linphone/view/Page/Window/Main/MainWindow.qml index a5b9265a2..8f03bbb85 100644 --- a/Linphone/view/Page/Window/Main/MainWindow.qml +++ b/Linphone/view/Page/Window/Main/MainWindow.qml @@ -60,6 +60,10 @@ AbstractWindow { openMainPage() mainWindowStackView.currentItem.displayChatPage(contactAddress) } + function openChat(chat) { + openMainPage() + mainWindowStackView.currentItem.openChat(chat) + } function transferCallSucceed() { openMainPage() //: "Appel transféré"