From b79b324027f1176ab3a4e0b421bb2131b11e1c31 Mon Sep 17 00:00:00 2001 From: Gaelle Braud Date: Tue, 29 Apr 2025 17:30:04 +0200 Subject: [PATCH] open chat room from call/conf navigate to chat from contact/history/magic search select chat when joining from contact/history/magic search message notification filter chat rooms --- Linphone/core/chat/ChatCore.cpp | 19 ++- Linphone/core/chat/ChatCore.hpp | 6 + Linphone/core/chat/ChatList.cpp | 55 ++++--- Linphone/core/chat/ChatList.hpp | 4 +- Linphone/core/chat/ChatProxy.cpp | 21 ++- Linphone/core/chat/ChatProxy.hpp | 2 + .../core/chat/message/ChatMessageCore.cpp | 13 +- .../core/chat/message/ChatMessageCore.hpp | 7 + Linphone/core/notifier/Notifier.cpp | 102 ++++++++----- Linphone/core/notifier/Notifier.hpp | 7 +- Linphone/core/search/MagicSearchProxy.cpp | 12 +- Linphone/model/call/CallModel.cpp | 6 +- Linphone/model/call/CallModel.hpp | 2 + Linphone/model/chat/ChatModel.cpp | 12 +- Linphone/model/chat/ChatModel.hpp | 1 + .../model/chat/message/ChatMessageModel.cpp | 6 +- .../model/chat/message/ChatMessageModel.hpp | 2 + Linphone/model/conference/ConferenceModel.cpp | 7 +- Linphone/model/conference/ConferenceModel.hpp | 2 + Linphone/model/core/CoreModel.cpp | 18 ++- Linphone/model/core/CoreModel.hpp | 8 +- Linphone/model/tool/ToolModel.cpp | 141 ++++++++++++++++-- Linphone/model/tool/ToolModel.hpp | 7 + Linphone/tool/Utils.cpp | 72 +++++++++ Linphone/tool/Utils.hpp | 9 +- Linphone/view/CMakeLists.txt | 1 + Linphone/view/Control/Button/Button.qml | 3 +- .../Container/Call/CallHistoryLayout.qml | 7 +- .../Control/Container/Main/MainRightPanel.qml | 1 + .../Control/Display/Chat/ChatListView.qml | 37 ++--- .../Display/Chat/ChatMessagesListView.qml | 4 + .../Display/Contact/ContactListItem.qml | 4 + Linphone/view/Control/Popup/DesktopPopup.qml | 2 +- .../NotificationReceivedMessage.qml | 118 +++++++++++++++ .../view/Page/Form/Chat/SelectedChatView.qml | 16 +- Linphone/view/Page/Layout/Main/MainLayout.qml | 17 ++- Linphone/view/Page/Main/Call/CallPage.qml | 7 +- .../view/Page/Main/Call/CallSettingsPanel.qml | 2 +- Linphone/view/Page/Main/Chat/ChatPage.qml | 11 ++ .../view/Page/Main/Contact/ContactPage.qml | 16 +- .../view/Page/Window/Call/CallsWindow.qml | 52 +++++++ Linphone/view/Page/Window/Main/MainWindow.qml | 4 + Linphone/view/Style/buttonStyle.js | 3 +- 43 files changed, 692 insertions(+), 154 deletions(-) create mode 100644 Linphone/view/Control/Popup/Notification/NotificationReceivedMessage.qml diff --git a/Linphone/core/chat/ChatCore.cpp b/Linphone/core/chat/ChatCore.cpp index 20b83fcab..02ffe9c18 100644 --- a/Linphone/core/chat/ChatCore.cpp +++ b/Linphone/core/chat/ChatCore.cpp @@ -43,17 +43,17 @@ ChatCore::ChatCore(const std::shared_ptr &chatRoom) : QObjec mLastUpdatedTime = QDateTime::fromSecsSinceEpoch(chatRoom->getLastUpdateTime()); if (chatRoom->hasCapability((int)linphone::ChatRoom::Capabilities::Basic)) { mTitle = ToolModel::getDisplayName(chatRoom->getPeerAddress()->clone()); - mAvatarUri = Utils::coreStringToAppString(chatRoom->getPeerAddress()->asStringUriOnly()); + mAvatarUri = ToolModel::getDisplayName(chatRoom->getPeerAddress()->clone()); auto peerAddress = chatRoom->getPeerAddress(); mPeerAddress = Utils::coreStringToAppString(peerAddress->asStringUriOnly()); } else { if (chatRoom->hasCapability((int)linphone::ChatRoom::Capabilities::OneToOne)) { - auto peer = chatRoom->getParticipants().front(); - if (peer) mTitle = ToolModel::getDisplayName(peer->getAddress()->clone()); - mAvatarUri = Utils::coreStringToAppString(peer->getAddress()->asStringUriOnly()); auto participants = chatRoom->getParticipants(); + auto peer = participants.front(); + if (peer) mTitle = ToolModel::getDisplayName(peer->getAddress()->clone()); + mAvatarUri = ToolModel::getDisplayName(peer->getAddress()->clone()); if (participants.size() == 1) { - auto peerAddress = participants.front()->getAddress(); + auto peerAddress = peer->getAddress(); if (peerAddress) mPeerAddress = Utils::coreStringToAppString(peerAddress->asStringUriOnly()); } } else if (chatRoom->hasCapability((int)linphone::ChatRoom::Capabilities::Conference)) { @@ -75,6 +75,7 @@ ChatCore::ChatCore(const std::shared_ptr &chatRoom) : QObjec auto chatMessage = ChatMessageCore::create(message); mChatMessageList.append(chatMessage); } + mIdentifier = Utils::coreStringToAppString(chatRoom->getIdentifier()); } ChatCore::~ChatCore() { @@ -121,6 +122,10 @@ void ChatCore::setTitle(QString title) { } } +QString ChatCore::getIdentifier() const { + return mIdentifier; +} + QString ChatCore::getPeerAddress() const { return mPeerAddress; } @@ -193,3 +198,7 @@ void ChatCore::removeMessagesFromMessageList(QList 0) emit messageListChanged(); } + +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 89c22b255..9d782d9fc 100644 --- a/Linphone/core/chat/ChatCore.hpp +++ b/Linphone/core/chat/ChatCore.hpp @@ -35,6 +35,7 @@ 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 avatarUri READ getAvatarUri WRITE setAvatarUri NOTIFY avatarUriChanged) Q_PROPERTY(QDateTime lastUpdatedTime READ getLastUpdatedTime WRITE setLastUpdatedTime NOTIFY lastUpdatedTimeChanged) @@ -56,6 +57,8 @@ public: QString getTitle() const; void setTitle(QString title); + QString getIdentifier() const; + QString getLastMessageInHistory() const; void setLastMessageInHistory(QString message); @@ -73,6 +76,8 @@ public: QString getAvatarUri() const; void setAvatarUri(QString avatarUri); + std::shared_ptr getModel() const; + signals: void lastUpdatedTimeChanged(QDateTime time); void lastMessageInHistoryChanged(QString time); @@ -88,6 +93,7 @@ private: QString mLastMessageInHistory; QString mPeerAddress; QString mTitle; + QString mIdentifier; QString mAvatarUri; int mUnreadMessagesCount; std::shared_ptr mChatModel; diff --git a/Linphone/core/chat/ChatList.cpp b/Linphone/core/chat/ChatList.cpp index 7429290b9..71330ea48 100644 --- a/Linphone/core/chat/ChatList.cpp +++ b/Linphone/core/chat/ChatList.cpp @@ -61,8 +61,7 @@ void ChatList::setSelf(QSharedPointer me) { // Avoid copy to lambdas QList> *chats = new QList>(); auto currentAccount = CoreModel::getInstance()->getCore()->getDefaultAccount(); -// auto linphoneChatRooms = currentAccount->filterChatRooms(Utils::appStringToCoreString(mFilter)); - auto linphoneChatRooms = currentAccount->getChatRooms(); + auto linphoneChatRooms = currentAccount->filterChatRooms(Utils::appStringToCoreString(mFilter)); for (auto it : linphoneChatRooms) { auto model = createChatCore(it); chats->push_back(model); @@ -75,25 +74,29 @@ void ChatList::setSelf(QSharedPointer me) { }); }); - mModelConnection->makeConnectToModel(&CoreModel::chatRoomStateChanged, - [this](const std::shared_ptr &core, - const std::shared_ptr &chatRoom, - linphone::ChatRoom::State state) { - // check account, filtre, puis ajout si c'est bon - bool toCreate = false; - if (toCreate) { - auto model = createChatCore(chatRoom); - mModelConnection->invokeToCore([this, model]() { - // We set the current here and not on firstChatStarted event - // because we don't want to add unicity check while keeping the - // same model between list and current chat. - add(model); - }); - } - }); - mModelConnection->makeConnectToModel(&CoreModel::defaultAccountChanged, [this] (std::shared_ptr core, std::shared_ptr account) { - lUpdate(); - }); + mModelConnection->makeConnectToModel( + &CoreModel::chatRoomStateChanged, + [this](const std::shared_ptr &core, const std::shared_ptr &chatRoom, + linphone::ChatRoom::State state) { + // check account, filtre, puis ajout si c'est bon + if (chatRoom->getAccount() == core->getDefaultAccount()) { + if (state == linphone::ChatRoom::State::Created) { + auto list = getSharedList(); + auto found = + std::find_if(list.begin(), list.end(), [chatRoom](const QSharedPointer &item) { + return (item && item->getModel()->getMonitor() == chatRoom); + }); + if (found != list.end()) { + qDebug() << "chat room created, add it to the list"; + auto model = createChatCore(chatRoom); + mModelConnection->invokeToCore([this, model]() { add(model); }); + } + } + } + }); + mModelConnection->makeConnectToModel( + &CoreModel::defaultAccountChanged, + [this](std::shared_ptr core, std::shared_ptr account) { lUpdate(); }); connect(this, &ChatList::filterChanged, [this](QString filter) { mFilter = filter; @@ -102,6 +105,16 @@ void ChatList::setSelf(QSharedPointer me) { lUpdate(); } +int ChatList::findChatIndex(ChatGui *chatGui) { + if (!chatGui) return -1; + auto core = chatGui->mCore; + auto chatList = getSharedList(); + auto it = std::find_if(chatList.begin(), chatList.end(), [core](const QSharedPointer item) { + return item->getIdentifier() == core->getIdentifier(); + }); + return it == chatList.end() ? -1 : std::distance(chatList.begin(), it); +} + QVariant ChatList::data(const QModelIndex &index, int role) const { int row = index.row(); if (!index.isValid() || row < 0 || row >= mList.count()) return QVariant(); diff --git a/Linphone/core/chat/ChatList.hpp b/Linphone/core/chat/ChatList.hpp index 8202482a3..ec2a9f9b6 100644 --- a/Linphone/core/chat/ChatList.hpp +++ b/Linphone/core/chat/ChatList.hpp @@ -38,9 +38,11 @@ public: QSharedPointer createChatCore(const std::shared_ptr &chatroom); ChatList(QObject *parent = Q_NULLPTR); ~ChatList(); - void setSelf(QSharedPointer me); + + int findChatIndex(ChatGui *chat); virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + signals: void lUpdate(); void filterChanged(QString filter); diff --git a/Linphone/core/chat/ChatProxy.cpp b/Linphone/core/chat/ChatProxy.cpp index 589c853d9..8578a3c6c 100644 --- a/Linphone/core/chat/ChatProxy.cpp +++ b/Linphone/core/chat/ChatProxy.cpp @@ -40,15 +40,30 @@ void ChatProxy::setSourceModel(QAbstractItemModel *model) { } auto newChatList = dynamic_cast(model); if (newChatList) { -// connect(this, &ChatProxy::filterTextChanged, newChatList, &ChatList::filterChanged); + connect(this, &ChatProxy::filterTextChanged, newChatList, + [this, newChatList] { emit newChatList->filterChanged(getFilterText()); }); } setSourceModels(new SortFilterList(model)); sort(0); } +int ChatProxy::findChatIndex(ChatGui *chatGui) { + auto chatList = getListModel(); + if (chatList) { + auto listIndex = chatList->findChatIndex(chatGui); + if (listIndex != -1) { + listIndex = + dynamic_cast(sourceModel())->mapFromSource(chatList->index(listIndex, 0)).row(); + if (mMaxDisplayItems <= listIndex) setMaxDisplayItems(listIndex + mDisplayItemsStep); + return listIndex; + } + } + return -1; +} + bool ChatProxy::SortFilterList::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const { -// auto l = getItemAtSource(sourceRow); -// return l != nullptr; + // auto l = getItemAtSource(sourceRow); + // return l != nullptr; return true; } diff --git a/Linphone/core/chat/ChatProxy.hpp b/Linphone/core/chat/ChatProxy.hpp index f68de29d5..152e84135 100644 --- a/Linphone/core/chat/ChatProxy.hpp +++ b/Linphone/core/chat/ChatProxy.hpp @@ -39,6 +39,8 @@ public: void setSourceModel(QAbstractItemModel *sourceModel) override; + Q_INVOKABLE int findChatIndex(ChatGui *chatGui); + protected: QSharedPointer mList; DECLARE_ABSTRACT_OBJECT diff --git a/Linphone/core/chat/message/ChatMessageCore.cpp b/Linphone/core/chat/message/ChatMessageCore.cpp index ed076725b..41f3dd42a 100644 --- a/Linphone/core/chat/message/ChatMessageCore.cpp +++ b/Linphone/core/chat/message/ChatMessageCore.cpp @@ -20,6 +20,7 @@ #include "ChatMessageCore.hpp" #include "core/App.hpp" +#include "model/tool/ToolModel.hpp" DEFINE_ABSTRACT_OBJECT(ChatMessageCore) @@ -31,7 +32,7 @@ QSharedPointer ChatMessageCore::create(const std::shared_ptr
  • &chatmessage) { - lDebug() << "[ChatMessageCore] new" << this; + // lDebug() << "[ChatMessageCore] new" << this; mustBeInLinphoneThread(getClassName()); App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::CppOwnership); mChatMessageModel = Utils::makeQObject_ptr(chatmessage); @@ -41,6 +42,8 @@ ChatMessageCore::ChatMessageCore(const std::shared_ptr &c auto from = chatmessage->getFromAddress(); auto to = chatmessage->getLocalAddress(); mIsRemoteMessage = !from->weakEqual(to); + mPeerAddress = Utils::coreStringToAppString(chatmessage->getPeerAddress()->asStringUriOnly()); + mPeerName = ToolModel::getDisplayName(chatmessage->getPeerAddress()->clone()); } ChatMessageCore::~ChatMessageCore() { @@ -72,6 +75,14 @@ void ChatMessageCore::setText(QString text) { } } +QString ChatMessageCore::getPeerAddress() const { + return mPeerAddress; +} + +QString ChatMessageCore::getPeerName() const { + return mPeerName; +} + bool ChatMessageCore::isRemoteMessage() const { return mIsRemoteMessage; } diff --git a/Linphone/core/chat/message/ChatMessageCore.hpp b/Linphone/core/chat/message/ChatMessageCore.hpp index 646781ea6..75375b60a 100644 --- a/Linphone/core/chat/message/ChatMessageCore.hpp +++ b/Linphone/core/chat/message/ChatMessageCore.hpp @@ -33,6 +33,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 peerAddress READ getPeerAddress CONSTANT) + Q_PROPERTY(QString peerName READ getPeerName CONSTANT) Q_PROPERTY(bool isRemoteMessage READ isRemoteMessage WRITE setIsRemoteMessage NOTIFY isRemoteMessageChanged) public: @@ -47,6 +49,9 @@ public: QString getText() const; void setText(QString text); + QString getPeerAddress() const; + QString getPeerName() const; + bool isRemoteMessage() const; void setIsRemoteMessage(bool isRemote); @@ -58,6 +63,8 @@ signals: private: DECLARE_ABSTRACT_OBJECT QString mText; + QString mPeerAddress; + QString mPeerName; QDateTime mTimestamp; bool mIsRemoteMessage = false; std::shared_ptr mChatMessageModel; diff --git a/Linphone/core/notifier/Notifier.cpp b/Linphone/core/notifier/Notifier.cpp index 20ff59d72..54a04029f 100644 --- a/Linphone/core/notifier/Notifier.cpp +++ b/Linphone/core/notifier/Notifier.cpp @@ -33,6 +33,7 @@ #include "core/App.hpp" #include "core/call/CallGui.hpp" +#include "core/chat/ChatCore.hpp" #include "model/tool/ToolModel.hpp" #include "tool/LinphoneEnums.hpp" #include "tool/providers/AvatarProvider.hpp" @@ -85,9 +86,9 @@ void setProperty(QObject &object, const char *property, const T &value) { // ============================================================================= const QHash Notifier::Notifications = { - //{Notifier::ReceivedMessage, {Notifier::ReceivedMessage, "NotificationReceivedMessage.qml", 10}}, + {Notifier::ReceivedMessage, {Notifier::ReceivedMessage, "NotificationReceivedMessage.qml", 10}}, //{Notifier::ReceivedFileMessage, {Notifier::ReceivedFileMessage, "NotificationReceivedFileMessage.qml", 10}}, - {Notifier::ReceivedCall, {Notifier::ReceivedCall, "NotificationReceivedCall.qml", 30}}, + {Notifier::ReceivedCall, {Notifier::ReceivedCall, "NotificationReceivedCall.qml", 30}} //{Notifier::NewVersionAvailable, {Notifier::NewVersionAvailable, "NotificationNewVersionAvailable.qml", 30}}, //{Notifier::SnapshotWasTaken, {Notifier::SnapshotWasTaken, "NotificationSnapshotWasTaken.qml", 10}}, //{Notifier::RecordingCompleted, {Notifier::RecordingCompleted, "NotificationRecordingCompleted.qml", 10}} @@ -127,7 +128,7 @@ Notifier::~Notifier() { bool Notifier::createNotification(Notifier::NotificationType type, QVariantMap data) { mMutex->lock(); - Q_ASSERT(mInstancesNumber <= MaxNotificationsNumber); + // Q_ASSERT(mInstancesNumber <= MaxNotificationsNumber); if (mInstancesNumber == MaxNotificationsNumber) { // Check existing instances. qWarning() << QStringLiteral("Unable to create another notification."); mMutex->unlock(); @@ -305,43 +306,68 @@ void Notifier::notifyReceivedCall(const shared_ptr &call) { }); } -/* -void Notifier::notifyReceivedMessages(const list> &messages) { - QVariantMap map; - QString txt; - if (messages.size() > 0) { - shared_ptr message = messages.front(); +void Notifier::notifyReceivedMessages(const std::shared_ptr &room, + const list> &messages) { + mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); - if (messages.size() == 1) { - auto fileContent = message->getFileTransferInformation(); - if (!fileContent) { - foreach (auto content, message->getContents()) { - if (content->isText()) txt += content->getUtf8Text().c_str(); - } - } else if (fileContent->isVoiceRecording()) - //: 'Voice message received!' : message to warn the user in a notofication for voice messages. - txt = tr("new_voice_message"); - else txt = tr("new_file_message"); - if (txt.isEmpty() && message->hasConferenceInvitationContent()) - //: 'Conference invitation received!' : Notification about receiving an invitation to a conference. - txt = tr("new_conference_invitation"); - } else - //: 'New messages received!' Notification that warn the user of new messages. - txt = tr("new_chat_room_messages"); - map["message"] = txt; - shared_ptr chatRoom(message->getChatRoom()); - map["timelineModel"].setValue( - CoreManager::getInstance()->getTimelineListModel()->getTimeline(chatRoom, true).get()); - if (messages.size() == 1) { // Display only sender on mono message. - map["remoteAddress"] = Utils::coreStringToAppString(message->getFromAddress()->asStringUriOnly()); - map["fullremoteAddress"] = Utils::coreStringToAppString(message->getFromAddress()->asString()); - } - map["localAddress"] = Utils::coreStringToAppString(message->getToAddress()->asStringUriOnly()); - map["fullLocalAddress"] = Utils::coreStringToAppString(message->getToAddress()->asString()); - map["window"].setValue(App::getInstance()->getMainWindow()); - CREATE_NOTIFICATION(Notifier::ReceivedMessage, map) - } + QString txt; + QString remoteAddress; + + if (messages.size() > 0) { + shared_ptr message = messages.front(); + + auto receiverAccount = ToolModel::findAccount(message->getToAddress()); + if (receiverAccount) { + auto senderAccount = ToolModel::findAccount(message->getFromAddress()); + if (senderAccount) { + return; + } + auto accountModel = Utils::makeQObject_ptr(receiverAccount); + accountModel->setSelf(accountModel); + if (!accountModel->getNotificationsAllowed()) { + qInfo() << "Notifications have been disabled for this account - not creating a notification for " + "incoming message"; + return; + } + } + + if (messages.size() == 1) { // Display only sender on mono message. + auto remoteAddr = message->getFromAddress()->clone(); + remoteAddr->clean(); + remoteAddress = Utils::coreStringToAppString(remoteAddr->asStringUriOnly()); + auto fileContent = message->getFileTransferInformation(); + if (!fileContent) { + foreach (auto content, message->getContents()) { + if (content->isText()) txt += content->getUtf8Text().c_str(); + } + } else if (fileContent->isVoiceRecording()) + //: 'Voice message received!' : message to warn the user in a notofication for voice messages. + txt = tr("new_voice_message"); + else txt = tr("new_file_message"); + if (txt.isEmpty() && message->hasConferenceInvitationContent()) + //: 'Conference invitation received!' : Notification about receiving an invitation to a conference. + txt = tr("new_conference_invitation"); + } else { + //: 'New messages received!' Notification that warn the user of new messages. + txt = tr("new_chat_room_messages"); + } + + auto chatCore = ChatCore::create(room); + + App::postCoreAsync([this, txt, chatCore, remoteAddress]() { + 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["avatarUri"] = chatCore->getAvatarUri(); + CREATE_NOTIFICATION(Notifier::ReceivedMessage, map) + }); + } } +/* void Notifier::notifyReceivedReactions( const QList, std::shared_ptr>> diff --git a/Linphone/core/notifier/Notifier.hpp b/Linphone/core/notifier/Notifier.hpp index 7eaf16a91..088c9de0b 100644 --- a/Linphone/core/notifier/Notifier.hpp +++ b/Linphone/core/notifier/Notifier.hpp @@ -41,9 +41,9 @@ public: ~Notifier(); enum NotificationType { - // ReceivedMessage, + ReceivedMessage, // ReceivedFileMessage, - ReceivedCall, + ReceivedCall // NewVersionAvailable, // SnapshotWasTaken, // RecordingCompleted @@ -52,8 +52,9 @@ public: // void notifyReceivedCall(Call *call); void notifyReceivedCall(const std::shared_ptr &call); // Call from Linphone + void notifyReceivedMessages(const std::shared_ptr &room, + const std::list> &messages); /* - void notifyReceivedMessages(const std::list> &messages); void notifyReceivedReactions( const QList, std::shared_ptr>> &reactions); void notifyReceivedFileMessage(const diff --git a/Linphone/core/search/MagicSearchProxy.cpp b/Linphone/core/search/MagicSearchProxy.cpp index df042f5fb..106f6b4cf 100644 --- a/Linphone/core/search/MagicSearchProxy.cpp +++ b/Linphone/core/search/MagicSearchProxy.cpp @@ -110,11 +110,13 @@ int MagicSearchProxy::loadUntil(const QString &address) { auto magicSearchList = getListModel(); if (magicSearchList) { auto listIndex = magicSearchList->findFriendIndexByAddress(address); - if (listIndex == -1) return -1; - listIndex = - dynamic_cast(sourceModel())->mapFromSource(magicSearchList->index(listIndex, 0)).row(); - if (mMaxDisplayItems <= listIndex) setMaxDisplayItems(listIndex + mDisplayItemsStep); - return listIndex; + if (listIndex != -1) { + listIndex = dynamic_cast(sourceModel()) + ->mapFromSource(magicSearchList->index(listIndex, 0)) + .row(); + if (mMaxDisplayItems <= listIndex) setMaxDisplayItems(listIndex + mDisplayItemsStep); + return listIndex; + } } return -1; } diff --git a/Linphone/model/call/CallModel.cpp b/Linphone/model/call/CallModel.cpp index 4dd213edb..bd36c1da3 100644 --- a/Linphone/model/call/CallModel.cpp +++ b/Linphone/model/call/CallModel.cpp @@ -233,6 +233,10 @@ bool CallModel::getZrtpCaseMismatch() const { return mMonitor->getZrtpCacheMismatchFlag(); } +std::shared_ptr CallModel::getConference() const { + return mMonitor->getConference(); +} + void CallModel::setConference(const std::shared_ptr &conference) { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); if (mConference != conference) { @@ -411,7 +415,7 @@ void CallModel::onStateChanged(const std::shared_ptr &call, emit localVideoEnabledChanged(videoDirection == linphone::MediaDirection::SendOnly || videoDirection == linphone::MediaDirection::SendRecv); emit remoteVideoEnabledChanged(remoteVideoDirection == linphone::MediaDirection::SendOnly || - remoteVideoDirection == linphone::MediaDirection::SendRecv); + remoteVideoDirection == linphone::MediaDirection::SendRecv); updateConferenceVideoLayout(); } else if (state == linphone::Call::State::End || state == linphone::Call::State::Error) { mDurationTimer.stop(); diff --git a/Linphone/model/call/CallModel.hpp b/Linphone/model/call/CallModel.hpp index e2712842c..847f50f41 100644 --- a/Linphone/model/call/CallModel.hpp +++ b/Linphone/model/call/CallModel.hpp @@ -72,6 +72,8 @@ public: QStringList getRemoteAtuhenticationTokens() const; bool getZrtpCaseMismatch() const; + std::shared_ptr getConference() const; + LinphoneEnums::ConferenceLayout getConferenceVideoLayout() const; void changeConferenceVideoLayout(LinphoneEnums::ConferenceLayout layout); // Make a call request void updateConferenceVideoLayout(); // Called from call state changed ater the new layout has been set. diff --git a/Linphone/model/chat/ChatModel.cpp b/Linphone/model/chat/ChatModel.cpp index 1d34005eb..196140463 100644 --- a/Linphone/model/chat/ChatModel.cpp +++ b/Linphone/model/chat/ChatModel.cpp @@ -54,6 +54,10 @@ std::list> ChatModel::getHistory() const return res; } +QString ChatModel::getIdentifier() const { + return Utils::coreStringToAppString(mMonitor->getIdentifier()); +} + QString ChatModel::getTitle() { if (mMonitor->hasCapability((int)linphone::ChatRoom::Capabilities::Basic)) { return ToolModel::getDisplayName(mMonitor->getPeerAddress()->clone()); @@ -65,6 +69,7 @@ QString ChatModel::getTitle() { return Utils::coreStringToAppString(mMonitor->getSubject()); } } + return QString(); } QString ChatModel::getPeerAddress() const { @@ -101,15 +106,14 @@ void ChatModel::onIsComposingReceived(const std::shared_ptr emit isComposingReceived(chatRoom, remoteAddress, isComposing); } -// Do not use this api, only manipulate EventLogs void ChatModel::onMessageReceived(const std::shared_ptr &chatRoom, const std::shared_ptr &message) { - // emit messageReceived(chatRoom, message); + emit messageReceived(chatRoom, message); } void ChatModel::onMessagesReceived(const std::shared_ptr &chatRoom, const std::list> &chatMessages) { - // emit messagesReceived(chatRoom, chatMessages); + emit messagesReceived(chatRoom, chatMessages); } void ChatModel::onNewEvent(const std::shared_ptr &chatRoom, @@ -258,5 +262,5 @@ void ChatModel::onChatRoomRead(const std::shared_ptr &chatRo void ChatModel::onNewMessageReaction(const std::shared_ptr &chatRoom, const std::shared_ptr &message, const std::shared_ptr &reaction) { - emit onNewMessageReaction(chatRoom, message, reaction); + // emit onNewMessageReaction(chatRoom, message, reaction); } diff --git a/Linphone/model/chat/ChatModel.hpp b/Linphone/model/chat/ChatModel.hpp index eb3ef7170..77fbc3e1b 100644 --- a/Linphone/model/chat/ChatModel.hpp +++ b/Linphone/model/chat/ChatModel.hpp @@ -43,6 +43,7 @@ public: QString getLastMessageInHistory(std::list> startList = {}) const; int getUnreadMessagesCount() const; std::list> getHistory() const; + QString getIdentifier() const; private: DECLARE_ABSTRACT_OBJECT diff --git a/Linphone/model/chat/message/ChatMessageModel.cpp b/Linphone/model/chat/message/ChatMessageModel.cpp index da20a9503..2292bdfac 100644 --- a/Linphone/model/chat/message/ChatMessageModel.cpp +++ b/Linphone/model/chat/message/ChatMessageModel.cpp @@ -32,7 +32,7 @@ DEFINE_ABSTRACT_OBJECT(ChatMessageModel) ChatMessageModel::ChatMessageModel(const std::shared_ptr &chatMessage, QObject *parent) : ::Listener(chatMessage, parent) { - lDebug() << "[ChatMessageModel] new" << this << " / SDKModel=" << chatMessage.get(); + // lDebug() << "[ChatMessageModel] new" << this << " / SDKModel=" << chatMessage.get(); mustBeInLinphoneThread(getClassName()); } @@ -44,6 +44,10 @@ QString ChatMessageModel::getText() const { return ToolModel::getMessageFromContent(mMonitor->getContents()); } +QString ChatMessageModel::getPeerAddress() const { + return Utils::coreStringToAppString(mMonitor->getPeerAddress()->asStringUriOnly()); +} + QDateTime ChatMessageModel::getTimestamp() const { return QDateTime::fromSecsSinceEpoch(mMonitor->getTime()); } diff --git a/Linphone/model/chat/message/ChatMessageModel.hpp b/Linphone/model/chat/message/ChatMessageModel.hpp index d1a3fe041..2457b01ca 100644 --- a/Linphone/model/chat/message/ChatMessageModel.hpp +++ b/Linphone/model/chat/message/ChatMessageModel.hpp @@ -40,6 +40,8 @@ public: QString getText() const; QDateTime getTimestamp() const; + QString getPeerAddress() const; + private: DECLARE_ABSTRACT_OBJECT virtual std::shared_ptr onFileTransferSend(const std::shared_ptr &message, diff --git a/Linphone/model/conference/ConferenceModel.cpp b/Linphone/model/conference/ConferenceModel.cpp index 9f986c855..81cc68ad0 100644 --- a/Linphone/model/conference/ConferenceModel.cpp +++ b/Linphone/model/conference/ConferenceModel.cpp @@ -76,6 +76,10 @@ int ConferenceModel::getParticipantDeviceCount() const { return mMonitor->getParticipantDeviceList().size(); } +std::shared_ptr ConferenceModel::getChatRoom() const { + return mMonitor->getChatRoom(); +} + void ConferenceModel::setMicrophoneMuted(bool isMuted) { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); mMonitor->setMicrophoneMuted(isMuted); @@ -171,7 +175,8 @@ bool ConferenceModel::isScreenSharingEnabled() const { void ConferenceModel::onActiveSpeakerParticipantDevice( const std::shared_ptr &conference, const std::shared_ptr &participantDevice) { - lDebug() << "onActiveSpeakerParticipantDevice: " << (participantDevice ? participantDevice->getAddress()->asString().c_str() : "NULL"); + lDebug() << "onActiveSpeakerParticipantDevice: " + << (participantDevice ? participantDevice->getAddress()->asString().c_str() : "NULL"); emit activeSpeakerParticipantDevice(conference, conference->getActiveSpeakerParticipantDevice()); } diff --git a/Linphone/model/conference/ConferenceModel.hpp b/Linphone/model/conference/ConferenceModel.hpp index cbd08da91..23f9aa087 100644 --- a/Linphone/model/conference/ConferenceModel.hpp +++ b/Linphone/model/conference/ConferenceModel.hpp @@ -57,6 +57,8 @@ public: void removeParticipant(const std::shared_ptr &address); void addParticipant(const std::shared_ptr &address); + std::shared_ptr getChatRoom() const; + int getParticipantDeviceCount() const; void onIsScreenSharingEnabledChanged(); diff --git a/Linphone/model/core/CoreModel.cpp b/Linphone/model/core/CoreModel.cpp index 451455197..d9c73348a 100644 --- a/Linphone/model/core/CoreModel.cpp +++ b/Linphone/model/core/CoreModel.cpp @@ -122,9 +122,8 @@ void CoreModel::start() { linphoneSearch->setLimitedSearch(true); mMagicSearch = Utils::makeQObject_ptr(linphoneSearch); mMagicSearch->setSelf(mMagicSearch); - connect(mMagicSearch.get(), &MagicSearchModel::searchResultsReceived, this, [this] { - emit magicSearchResultReceived(mMagicSearch->mLastSearch); - }); + connect(mMagicSearch.get(), &MagicSearchModel::searchResultsReceived, this, + [this] { emit magicSearchResultReceived(mMagicSearch->mLastSearch); }); } // ----------------------------------------------------------------------------- @@ -352,10 +351,11 @@ void CoreModel::migrate() { config->setInt(SettingsModel::UiSection, Constants::RcVersionName, Constants::RcVersionCurrent); } -void CoreModel::searchInMagicSearch(QString filter, int sourceFlags, - LinphoneEnums::MagicSearchAggregation aggregation, - int maxResults) { - mMagicSearch->search(filter, sourceFlags, aggregation, maxResults); +void CoreModel::searchInMagicSearch(QString filter, + int sourceFlags, + LinphoneEnums::MagicSearchAggregation aggregation, + int maxResults) { + mMagicSearch->search(filter, sourceFlags, aggregation, maxResults); } //--------------------------------------------------------------------------------------------------------------------------- @@ -503,12 +503,16 @@ void CoreModel::onMessageReceived(const std::shared_ptr &core, const std::shared_ptr &room, const std::shared_ptr &message) { emit unreadNotificationsChanged(); + std::list> messages; + messages.push_back(message); + App::getInstance()->getNotifier()->notifyReceivedMessages(room, messages); emit messageReceived(core, room, message); } void CoreModel::onMessagesReceived(const std::shared_ptr &core, const std::shared_ptr &room, const std::list> &messages) { emit unreadNotificationsChanged(); + App::getInstance()->getNotifier()->notifyReceivedMessages(room, messages); emit messagesReceived(core, room, messages); } diff --git a/Linphone/model/core/CoreModel.hpp b/Linphone/model/core/CoreModel.hpp index af6da1a09..5cd242a39 100644 --- a/Linphone/model/core/CoreModel.hpp +++ b/Linphone/model/core/CoreModel.hpp @@ -34,8 +34,8 @@ #include "model/cli/CliModel.hpp" #include "model/listener/Listener.hpp" #include "model/logger/LoggerModel.hpp" -#include "tool/AbstractObject.hpp" #include "model/search/MagicSearchModel.hpp" +#include "tool/AbstractObject.hpp" // ============================================================================= @@ -61,9 +61,9 @@ public: void migrate(); void searchInMagicSearch(QString filter, - int sourceFlags, - LinphoneEnums::MagicSearchAggregation aggregation, - int maxResults); + int sourceFlags, + LinphoneEnums::MagicSearchAggregation aggregation, + int maxResults); bool mEnd = false; diff --git a/Linphone/model/tool/ToolModel.cpp b/Linphone/model/tool/ToolModel.cpp index effd8f035..d5c063d54 100644 --- a/Linphone/model/tool/ToolModel.cpp +++ b/Linphone/model/tool/ToolModel.cpp @@ -120,20 +120,18 @@ std::shared_ptr ToolModel::findFriendByAddress(const QString & auto defaultFriendList = CoreModel::getInstance()->getCore()->getDefaultFriendList(); if (!defaultFriendList) return nullptr; auto linphoneAddr = ToolModel::interpretUrl(address); - if (linphoneAddr) - return ToolModel::findFriendByAddress(linphoneAddr); - else - return nullptr; + if (linphoneAddr) return ToolModel::findFriendByAddress(linphoneAddr); + else return nullptr; } std::shared_ptr ToolModel::findFriendByAddress(std::shared_ptr linphoneAddr) { auto friendsManager = FriendsManager::getInstance(); QString key = Utils::coreStringToAppString(linphoneAddr->asStringUriOnly()); if (friendsManager->isInKnownFriends(key)) { -// qDebug() << key << "have been found in known friend, return it"; + // qDebug() << key << "have been found in known friend, return it"; return friendsManager->getKnownFriendAtKey(key); - } else if (friendsManager->isInUnknownFriends(key)) { -// qDebug() << key << "have been found in unknown friend, return it"; + } else if (friendsManager->isInUnknownFriends(key)) { + // qDebug() << key << "have been found in unknown friend, return it"; return friendsManager->getUnknownFriendAtKey(key); } auto f = CoreModel::getInstance()->getCore()->findFriend(linphoneAddr); @@ -141,20 +139,21 @@ std::shared_ptr ToolModel::findFriendByAddress(std::shared_ptr if (friendsManager->isInUnknownFriends(key)) { friendsManager->removeUnknownFriend(key); } -// qDebug() << "found friend, add to known map"; + // qDebug() << "found friend, add to known map"; friendsManager->appendKnownFriend(linphoneAddr, f); } if (!f) { if (friendsManager->isInOtherAddresses(key)) { -// qDebug() << "A magic search has already be done for address" << key << "and nothing was found, return"; + // qDebug() << "A magic search has already be done for address" << key << "and nothing was found, + // return"; return nullptr; } friendsManager->appendOtherAddress(key); -// qDebug() << "Couldn't find friend" << linphoneAddr->asStringUriOnly() << "in core, use magic search"; + // qDebug() << "Couldn't find friend" << linphoneAddr->asStringUriOnly() << "in core, use magic search"; CoreModel::getInstance()->searchInMagicSearch(Utils::coreStringToAppString(linphoneAddr->asStringUriOnly()), - (int)linphone::MagicSearch::Source::LdapServers - | (int)linphone::MagicSearch::Source::RemoteCardDAV - , LinphoneEnums::MagicSearchAggregation::Friend, 50); + (int)linphone::MagicSearch::Source::LdapServers | + (int)linphone::MagicSearch::Source::RemoteCardDAV, + LinphoneEnums::MagicSearchAggregation::Friend, 50); } return f; } @@ -384,7 +383,6 @@ bool ToolModel::friendIsInFriendList(const std::shared_ptr return (it != friends.end()); } - QString ToolModel::getMessageFromContent(std::list> contents) { for (auto &content : contents) { if (content->isText()) { @@ -467,3 +465,118 @@ QString ToolModel::computeUserAgent(const std::shared_ptr &con .arg(qVersion()) .remove("'"); } + +std::shared_ptr +ToolModel::getChatRoomParams(std::shared_ptr call, std::shared_ptr remoteAddress) { + auto core = call ? call->getCore() : CoreModel::getInstance()->getCore(); + auto localAddress = call ? call->getCallLog()->getLocalAddress() : nullptr; + if (!remoteAddress && call) remoteAddress = call->getRemoteAddress()->clone(); + auto account = findAccount(localAddress); + if (!account) account = core->getDefaultAccount(); + if (!account) qWarning() << "failed to get account, return"; + if (!account) return nullptr; + + auto params = core->createConferenceParams(call ? call->getConference() : nullptr); + params->enableChat(true); + params->enableGroup(false); + //: Dummy subject + params->setSubject(Utils::appStringToCoreString(QObject::tr("chat_dummy_subject"))); + params->setAccount(account); + + auto chatParams = params->getChatParams(); + if (!chatParams) { + qWarning() << "failed to get chat params from conference params, return"; + return nullptr; + } + chatParams->setEphemeralLifetime(0); + auto accountParams = account->getParams(); + auto sameDomain = remoteAddress && remoteAddress->getDomain() == SettingsModel::getInstance()->getDefaultDomain() && + remoteAddress->getDomain() == accountParams->getDomain(); + if (accountParams->getInstantMessagingEncryptionMandatory() && sameDomain) { + qDebug() << "Account is in secure mode & domain matches, requesting E2E encryption"; + chatParams->setBackend(linphone::ChatRoom::Backend::FlexisipChat); + params->setSecurityLevel(linphone::Conference::SecurityLevel::EndToEnd); + } else if (!accountParams->getInstantMessagingEncryptionMandatory()) { + if (SettingsModel::getInstance()->getCreateEndToEndEncryptedMeetingsAndGroupCalls()) { + qDebug() << "Account is in interop mode but LIME is available, requesting E2E encryption"; + chatParams->setBackend(linphone::ChatRoom::Backend::FlexisipChat); + params->setSecurityLevel(linphone::Conference::SecurityLevel::EndToEnd); + } else { + qDebug() << "Account is in interop mode and LIME is available, disabling E2E encryption"; + chatParams->setBackend(linphone::ChatRoom::Backend::Basic); + params->setSecurityLevel(linphone::Conference::SecurityLevel::None); + } + } else { + qDebug() << "Account is in secure mode, can't chat with SIP address of different domain"; + return nullptr; + } + return params; +} + +std::shared_ptr ToolModel::lookupCurrentCallChat(std::shared_ptr callModel) { + auto call = callModel->getMonitor(); + auto conference = callModel->getConference(); + if (conference) { + return conference->getChatRoom(); + } else { + auto core = CoreModel::getInstance()->getCore(); + auto params = getChatRoomParams(call); + auto remoteaddress = call->getRemoteAddress(); + auto localAddress = call->getCallLog()->getLocalAddress(); + std::list> participants; + participants.push_back(remoteaddress->clone()); + qDebug() << "Looking for chat with local address" << localAddress->asStringUriOnly() << "and participant" + << remoteaddress->asStringUriOnly(); + auto existingChat = core->searchChatRoom(params, localAddress, nullptr, participants); + if (existingChat) qDebug() << "Found existing chat"; + else qDebug() << "Did not find existing chat"; + return existingChat; + } +} + +std::shared_ptr ToolModel::createCurrentCallChat(std::shared_ptr callModel) { + auto call = callModel->getMonitor(); + auto remoteAddress = call->getRemoteAddress(); + std::list> participants; + participants.push_back(remoteAddress->clone()); + auto core = CoreModel::getInstance()->getCore(); + auto params = getChatRoomParams(call); + if (!params) { + qWarning() << "failed to create chatroom params for call with" << remoteAddress->asStringUriOnly(); + return nullptr; + } + auto chatRoom = core->createChatRoom(params, participants); + return chatRoom; +} + +std::shared_ptr ToolModel::lookupChatForAddress(std::shared_ptr remoteAddress) { + auto core = CoreModel::getInstance()->getCore(); + auto account = core->getDefaultAccount(); + if (!account) return nullptr; + auto localAddress = account->getParams()->getIdentityAddress(); + if (!localAddress || !remoteAddress) return nullptr; + + auto params = getChatRoomParams(nullptr, remoteAddress); + std::list> participants; + participants.push_back(remoteAddress->clone()); + + qDebug() << "Looking for chat with local address" << localAddress->asStringUriOnly() << "and participant" + << remoteAddress->asStringUriOnly(); + auto existingChat = core->searchChatRoom(params, localAddress, nullptr, participants); + if (existingChat) qDebug() << "Found existing chat"; + else qDebug() << "Did not find existing chat"; + return existingChat; +} + +std::shared_ptr ToolModel::createChatForAddress(std::shared_ptr remoteAddress) { + std::list> participants; + participants.push_back(remoteAddress->clone()); + auto core = CoreModel::getInstance()->getCore(); + auto params = getChatRoomParams(nullptr, remoteAddress); + if (!params) { + qWarning() << "failed to create chatroom params for address" << remoteAddress->asStringUriOnly(); + return nullptr; + } + auto chatRoom = core->createChatRoom(params, participants); + return chatRoom; +} \ No newline at end of file diff --git a/Linphone/model/tool/ToolModel.hpp b/Linphone/model/tool/ToolModel.hpp index f6c4b40a8..d98aad65f 100644 --- a/Linphone/model/tool/ToolModel.hpp +++ b/Linphone/model/tool/ToolModel.hpp @@ -81,6 +81,13 @@ public: static QString getOsProduct(); static QString computeUserAgent(const std::shared_ptr &config); + static std::shared_ptr + getChatRoomParams(std::shared_ptr call, std::shared_ptr remoteAddress = nullptr); + static std::shared_ptr lookupCurrentCallChat(std::shared_ptr callModel); + static std::shared_ptr createCurrentCallChat(std::shared_ptr callModel); + static std::shared_ptr lookupChatForAddress(std::shared_ptr remoteAddress); + static std::shared_ptr createChatForAddress(std::shared_ptr remoteAddress); + private: DECLARE_ABSTRACT_OBJECT }; diff --git a/Linphone/tool/Utils.cpp b/Linphone/tool/Utils.cpp index 050d9cf8b..d5bafb700 100644 --- a/Linphone/tool/Utils.cpp +++ b/Linphone/tool/Utils.cpp @@ -22,6 +22,8 @@ #include "core/App.hpp" #include "core/call/CallGui.hpp" +#include "core/chat/ChatCore.hpp" +#include "core/chat/ChatGui.hpp" #include "core/conference/ConferenceCore.hpp" #include "core/conference/ConferenceInfoCore.hpp" #include "core/conference/ConferenceInfoGui.hpp" @@ -1508,6 +1510,76 @@ Utils::createFriendDeviceVariant(const QString &name, const QString &address, Li return map; } +VariantObject *Utils::getCurrentCallChat(CallGui *call) { + VariantObject *data = new VariantObject("lookupCurrentCallChat"); + if (!data) return nullptr; + if (!call || !call->mCore) return nullptr; + data->makeRequest([callModel = call->mCore->getModel(), data]() { + if (!callModel) return QVariant(); + auto linphoneChatRoom = ToolModel::lookupCurrentCallChat(callModel); + if (linphoneChatRoom) { + auto chatCore = ChatCore::create(linphoneChatRoom); + return QVariant::fromValue(new ChatGui(chatCore)); + } else { + qDebug() << "Did not find existing chat room, create one"; + linphoneChatRoom = ToolModel::createCurrentCallChat(callModel); + if (linphoneChatRoom != nullptr) { + qDebug() << "Chatroom created with" << callModel->getRemoteAddress()->asStringUriOnly(); + auto id = linphoneChatRoom->getIdentifier(); + auto params = linphoneChatRoom->getCurrentParams(); + auto chatCore = ChatCore::create(linphoneChatRoom); + return QVariant::fromValue(new ChatGui(chatCore)); + } else { + qWarning() << "Failed to create 1-1 conversation with" + << callModel->getRemoteAddress()->asStringUriOnly() << "!"; + //: Failed to create 1-1 conversation with %1 ! + data->mConnection->invokeToCore([] { + showInformationPopup(tr("information_popup_error_title"), + tr("information_popup_chatroom_creation_error_message"), false, + getCallsWindow()); + }); + return QVariant(); + } + } + }); + data->requestValue(); + return data; +} + +VariantObject *Utils::getChatForAddress(QString address) { + VariantObject *data = new VariantObject("lookupCurrentCallChat"); + if (!data) return nullptr; + data->makeRequest([address, data]() { + auto linAddr = ToolModel::interpretUrl(address); + if (!linAddr) return QVariant(); + auto linphoneChatRoom = ToolModel::lookupChatForAddress(linAddr); + if (linphoneChatRoom) { + auto chatCore = ChatCore::create(linphoneChatRoom); + return QVariant::fromValue(new ChatGui(chatCore)); + } else { + qDebug() << "Did not find existing chat room, create one"; + linphoneChatRoom = ToolModel::createChatForAddress(linAddr); + if (linphoneChatRoom != nullptr) { + qDebug() << "Chatroom created with" << linAddr->asStringUriOnly(); + auto params = linphoneChatRoom->getCurrentParams(); + auto chatCore = ChatCore::create(linphoneChatRoom); + return QVariant::fromValue(new ChatGui(chatCore)); + } else { + qWarning() << "Failed to create 1-1 conversation with" << linAddr->asStringUriOnly() << "!"; + //: Failed to create 1-1 conversation with %1 ! + data->mConnection->invokeToCore([] { + showInformationPopup(tr("information_popup_error_title"), + tr("information_popup_chatroom_creation_error_message"), false, + getCallsWindow()); + }); + return QVariant(); + } + } + }); + data->requestValue(); + return data; +} + // CLI void Utils::runCommandLine(const QString command) { diff --git a/Linphone/tool/Utils.hpp b/Linphone/tool/Utils.hpp index c6062e129..6b20893e9 100644 --- a/Linphone/tool/Utils.hpp +++ b/Linphone/tool/Utils.hpp @@ -51,6 +51,7 @@ class ConferenceInfoGui; class ConferenceCore; class ParticipantDeviceCore; class DownloadablePayloadTypeCore; +class ChatGui; class Utils : public QObject, public AbstractObject { Q_OBJECT @@ -88,8 +89,10 @@ public: Q_INVOKABLE static QString createAvatar(const QUrl &fileUrl); // Return the avatar path Q_INVOKABLE static QString formatElapsedTime(int seconds, bool dotsSeparator = true); // Return the elapsed time formated - Q_INVOKABLE static QString - formatDate(const QDateTime &date, bool includeTime = true, bool includeDateIfToday = true, QString format = ""); // Return the date formated + Q_INVOKABLE static QString formatDate(const QDateTime &date, + bool includeTime = true, + bool includeDateIfToday = true, + QString format = ""); // Return the date formated Q_INVOKABLE static QString formatDateElapsedTime(const QDateTime &date); Q_INVOKABLE static QString formatTime(const QDateTime &date); // Return the time formated Q_INVOKABLE static QStringList generateSecurityLettersArray(int arraySize, int correctIndex, QString correctCode); @@ -140,6 +143,8 @@ public: Q_INVOKABLE QList append(const QList a, const QList b); Q_INVOKABLE QString getAddressToDisplay(QVariantList addressList, QString filter, QString defaultAddress); + Q_INVOKABLE static VariantObject *getCurrentCallChat(CallGui *call); + Q_INVOKABLE static VariantObject *getChatForAddress(QString address); // QDir findDirectoryByName(QString startPath, QString name); static QString getApplicationProduct(); diff --git a/Linphone/view/CMakeLists.txt b/Linphone/view/CMakeLists.txt index 087dec775..4c4f5c359 100644 --- a/Linphone/view/CMakeLists.txt +++ b/Linphone/view/CMakeLists.txt @@ -88,6 +88,7 @@ list(APPEND _LINPHONEAPP_QML_FILES view/Control/Popup/Loading/LoadingPopup.qml view/Control/Popup/Notification/Notification.qml view/Control/Popup/Notification/NotificationReceivedCall.qml + view/Control/Popup/Notification/NotificationReceivedMessage.qml view/Control/Tool/MovableMouseArea.qml view/Control/Tool/Helper/utils.js diff --git a/Linphone/view/Control/Button/Button.qml b/Linphone/view/Control/Button/Button.qml index 58650ecdf..65af74487 100644 --- a/Linphone/view/Control/Button/Button.qml +++ b/Linphone/view/Control/Button/Button.qml @@ -32,6 +32,7 @@ Control.Button { property bool shadowEnabled: false property var contentImageColor: style?.image?.normal || DefaultStyle.main2_600 property var hoveredImageColor: style?.image?.pressed || Qt.darker(contentImageColor, 1.05) + property var checkedImageColor: style?.image?.checked || Qt.darker(contentImageColor, 1.1) property var pressedImageColor: style?.image?.pressed || Qt.darker(contentImageColor, 1.1) property bool asynchronous: false spacing: Math.round(5 * DefaultStyle.dp) @@ -126,7 +127,7 @@ Control.Button { imageWidth: mainItem.icon.width imageHeight: mainItem.icon.height colorizationColor: mainItem.checkable && mainItem.checked - ? mainItem.checkedColor || mainItem.pressedColor + ? mainItem.checkedImageColor || mainItem.checkedColor || mainItem.pressedColor : mainItem.pressed ? mainItem.pressedImageColor : mainItem.hovered diff --git a/Linphone/view/Control/Container/Call/CallHistoryLayout.qml b/Linphone/view/Control/Container/Call/CallHistoryLayout.qml index 41f597628..26d31e644 100644 --- a/Linphone/view/Control/Container/Call/CallHistoryLayout.qml +++ b/Linphone/view/Control/Container/Call/CallHistoryLayout.qml @@ -193,7 +193,12 @@ ColumnLayout { button.icon.source: AppIcons.chatTeardropText //: "Message" label: qsTr("contact_message_action") - button.onClicked: console.debug("[ContactLayout.qml] TODO : open conversation") + button.onClicked: { + console.debug("[CallHistoryLayout.qml] Open conversation") + if (mainItem.specificAddress === "") { + mainWindow.displayChatPage(mainItem.contact.core.defaultAddress) + } else mainWindow.displayChatPage(mainItem.specificAddress) + } } LabelButton { visible: !mainItem.isConference && SettingsCpp.videoEnabled diff --git a/Linphone/view/Control/Container/Main/MainRightPanel.qml b/Linphone/view/Control/Container/Main/MainRightPanel.qml index 079647afd..8eb976c9e 100644 --- a/Linphone/view/Control/Container/Main/MainRightPanel.qml +++ b/Linphone/view/Control/Container/Main/MainRightPanel.qml @@ -10,6 +10,7 @@ ColumnLayout { property color panelColor: DefaultStyle.grey_100 property alias headerContent: rightPanelHeader.children property alias content: rightPanelContent.children + property alias header: rightPanelHeader spacing: 0 Rectangle { diff --git a/Linphone/view/Control/Display/Chat/ChatListView.qml b/Linphone/view/Control/Display/Chat/ChatListView.qml index 627e8b0b1..7d03a1e9b 100644 --- a/Linphone/view/Control/Display/Chat/ChatListView.qml +++ b/Linphone/view/Control/Display/Chat/ChatListView.qml @@ -42,6 +42,11 @@ ListView { // flickDeceleration: 10000 spacing: Math.round(10 * DefaultStyle.dp) + function selectChat(chatGui) { + var index = chatProxy.findChatIndex(chatGui) + mainItem.currentIndex = index + } + Component.onCompleted: cacheBuffer = Math.max(contentHeight, 0) //contentHeight>0 ? contentHeight : 0// cache all items // remove binding loop onContentHeightChanged: Qt.callLater(function () { @@ -59,12 +64,13 @@ ListView { positionViewAtBeginning() // Stay at beginning } - onAtYEndChanged: { - if (atYEnd && count > 0) { - chatProxy.displayMore() - } - } - //---------------------------------------------------------------- + onAtYEndChanged: { + if (atYEnd && count > 0) { + chatProxy.displayMore() + } + } + +//---------------------------------------------------------------- function moveToCurrentItem() { if (mainItem.currentIndex >= 0) Utils.updatePosition(mainItem, mainItem) @@ -109,8 +115,8 @@ ListView { component UnreadNotification: Item { id: unreadNotif property int unread: 0 - width: Math.round(22 * DefaultStyle.dp) - height: Math.round(22 * DefaultStyle.dp) + width: Math.round(14 * DefaultStyle.dp) + height: Math.round(14 * DefaultStyle.dp) visible: unread > 0 Rectangle { id: background @@ -123,7 +129,7 @@ ListView { horizontalAlignment: Text.AlignHCenter color: DefaultStyle.grey_0 fontSizeMode: Text.Fit - font.pixelSize: Typography.p3.pixelSize + font.pixelSize: Math.round(10 * DefaultStyle.dp) text: parent.unreadNotif > 100 ? '99+' : unreadNotif.unread } } @@ -155,11 +161,7 @@ ListView { id: historyAvatar property var contactObj: UtilsCpp.findFriendByAddress(modelData.core.peerAddress) contact: contactObj?.value || null - onContactChanged: { - if (contact) console.log("found contact", contact.core.defaultAddress) - else console.log("no contact for peer address", modelData.core.peerAddress, modelData.core.avatarUri) - } - displayNameVal: contact ? undefined : modelData.core.avatarUri + displayNameVal: contact ? "" : modelData.core.avatarUri // secured: securityLevel === LinphoneEnums.SecurityLevel.EndToEndEncryptedAndVerified Layout.preferredWidth: Math.round(45 * DefaultStyle.dp) Layout.preferredHeight: Math.round(45 * DefaultStyle.dp) @@ -208,6 +210,7 @@ ListView { } RowLayout { + Item {Layout.fillWidth: true} UnreadNotification { id: unreadCount unread: modelData.core.unreadMessagesCount @@ -230,10 +233,8 @@ ListView { anchors.fill: parent opacity: 0.7 radius: Math.round(8 * DefaultStyle.dp) - color: mainItem.currentIndex - === index ? DefaultStyle.main2_200 : DefaultStyle.main2_100 - visible: mainItem.lastMouseContainsIndex === index - || mainItem.currentIndex === index + color: mainItem.currentIndex === index ? DefaultStyle.main2_200 : DefaultStyle.main2_100 + visible: mainItem.lastMouseContainsIndex === index || mainItem.currentIndex === index } onPressed: { mainItem.currentIndex = model.index diff --git a/Linphone/view/Control/Display/Chat/ChatMessagesListView.qml b/Linphone/view/Control/Display/Chat/ChatMessagesListView.qml index d1b9f94f0..5a7c6d874 100644 --- a/Linphone/view/Control/Display/Chat/ChatMessagesListView.qml +++ b/Linphone/view/Control/Display/Chat/ChatMessagesListView.qml @@ -19,6 +19,10 @@ ListView { chatGui: mainItem.chat } + header: Item { + height: Math.round(18 * DefaultStyle.dp) + } + delegate: ChatMessage { id: chatMessage width: Math.min(implicitWidth, Math.round(mainItem.width * (3/4))) diff --git a/Linphone/view/Control/Display/Contact/ContactListItem.qml b/Linphone/view/Control/Display/Contact/ContactListItem.qml index 73626327f..d39af0d2c 100644 --- a/Linphone/view/Control/Display/Contact/ContactListItem.qml +++ b/Linphone/view/Control/Display/Contact/ContactListItem.qml @@ -170,6 +170,10 @@ FocusScope { style: ButtonStyle.grey KeyNavigation.left: videoCallButton KeyNavigation.right: callButton + onClicked: { + console.debug("[ContactListItem.qml] Open conversation") + mainWindow.displayChatPage(mainItem.addressFromFilter) + } } } PopupButton { diff --git a/Linphone/view/Control/Popup/DesktopPopup.qml b/Linphone/view/Control/Popup/DesktopPopup.qml index 2ad569c8b..c41a222e1 100644 --- a/Linphone/view/Control/Popup/DesktopPopup.qml +++ b/Linphone/view/Control/Popup/DesktopPopup.qml @@ -7,7 +7,7 @@ import Qt.labs.platform // ============================================================================= - Window { +Window { id: mainItem // --------------------------------------------------------------------------- diff --git a/Linphone/view/Control/Popup/Notification/NotificationReceivedMessage.qml b/Linphone/view/Control/Popup/Notification/NotificationReceivedMessage.qml new file mode 100644 index 000000000..7ca2efa3f --- /dev/null +++ b/Linphone/view/Control/Popup/Notification/NotificationReceivedMessage.qml @@ -0,0 +1,118 @@ +import QtQuick +import QtQuick.Layouts +import Linphone +import UtilsCpp +import QtQuick.Controls as Control +import 'qrc:/qt/qml/Linphone/view/Style/buttonStyle.js' as ButtonStyle + +// ============================================================================= + +Notification { + id: mainItem + radius: Math.round(20 * DefaultStyle.dp) + backgroundColor: DefaultStyle.grey_600 + backgroundOpacity: 0.8 + overriddenWidth: Math.round(400 * DefaultStyle.dp) + overriddenHeight: content.height + + property string avatarUri: notificationData && notificationData.avatarUri + property string chatRoomName: notificationData && notificationData.chatRoomName + property string remoteAddress: notificationData && notificationData.remoteAddress + property string message: notificationData && notificationData.message + + Popup { + id: content + visible: mainItem.visible + width: parent.width + leftPadding: Math.round(18 * DefaultStyle.dp) + rightPadding: Math.round(18 * DefaultStyle.dp) + topPadding: Math.round(9 * DefaultStyle.dp) + bottomPadding: Math.round(18 * DefaultStyle.dp) + background: Item{} + contentItem: ColumnLayout { + spacing: Math.round(9 * DefaultStyle.dp) + RowLayout { + spacing: Math.round(4 * DefaultStyle.dp) + Layout.alignment: Qt.AlignHCenter + Image { + Layout.preferredWidth: Math.round(12 * DefaultStyle.dp) + Layout.preferredHeight: Math.round(12 * DefaultStyle.dp) + source: AppIcons.logo + } + Text { + text: "Linphone" + color: DefaultStyle.grey_0 + font { + pixelSize: Math.round(12 * DefaultStyle.dp) + weight: Typography.b3.weight + capitalization: Font.Capitalize + } + } + } + ColumnLayout { + spacing: Math.round(14 * DefaultStyle.dp) + Layout.alignment: Qt.AlignHCenter + Layout.fillWidth: true + RowLayout { + spacing: Math.round(10 * DefaultStyle.dp) + Avatar { + Layout.preferredWidth: Math.round(45 * DefaultStyle.dp) + Layout.preferredHeight: Math.round(45 * DefaultStyle.dp) + Layout.alignment: Qt.AlignHCenter + property var contactObj: UtilsCpp.findFriendByAddress(mainItem.remoteAddress) + contact: contactObj?.value || null + displayNameVal: contact ? "" : mainItem.avatarUri + } + ColumnLayout { + spacing: 0 + Text { + text: mainItem.chatRoomName + color: DefaultStyle.main2_200 + Layout.fillWidth: true + maximumLineCount: 1 + font { + pixelSize: Typography.h3.pixelSize + weight: Typography.h3.weight + capitalization: Font.Capitalize + } + } + Text { + text: mainItem.remoteAddress + color: DefaultStyle.main2_100 + Layout.fillWidth: true + maximumLineCount: 1 + font { + pixelSize: Typography.p1.pixelSize + weight: Typography.p1.weight + } + } + } + Item{Layout.fillWidth: true} + } + Rectangle { + Layout.fillWidth: true + Layout.preferredHeight: Math.round(60 * DefaultStyle.dp) + color: DefaultStyle.main2_400 + radius: Math.round(5 * DefaultStyle.dp) + Text { + anchors.fill: parent + anchors.leftMargin: 8 * DefaultStyle.dp + anchors.rightMargin: 8 * DefaultStyle.dp + anchors.topMargin: 8 * DefaultStyle.dp + anchors.bottomMargin: 8 * DefaultStyle.dp + verticalAlignment: Text.AlignVCenter + text: mainItem.message + maximumLineCount: 2 + color: DefaultStyle.grey_1000 + font { + pixelSize: Typography.p1s.pixelSize + weight: Typography.p1s.weight + italic: true + } + } + } + } + } + } + +} diff --git a/Linphone/view/Page/Form/Chat/SelectedChatView.qml b/Linphone/view/Page/Form/Chat/SelectedChatView.qml index 30dcc9b29..cd49f248c 100644 --- a/Linphone/view/Page/Form/Chat/SelectedChatView.qml +++ b/Linphone/view/Page/Form/Chat/SelectedChatView.qml @@ -12,21 +12,26 @@ import 'qrc:/qt/qml/Linphone/view/Style/buttonStyle.js' as ButtonStyle RowLayout { id: mainItem property ChatGui chat + property CallGui call + property alias callHeaderContent: splitPanel.headerContent spacing: 0 MainRightPanel { + id: splitPanel Layout.fillWidth: true Layout.fillHeight: true panelColor: DefaultStyle.grey_0 + header.visible: !mainItem.call clip: true headerContent: [ RowLayout { - anchors.left: parent.left - anchors.leftMargin: Math.round(31 * DefaultStyle.dp) - anchors.verticalCenter: parent.verticalCenter + anchors.left: parent?.left + anchors.leftMargin: mainItem.call ? 0 : Math.round(31 * DefaultStyle.dp) + anchors.verticalCenter: parent?.verticalCenter spacing: Math.round(12 * DefaultStyle.dp) Avatar { - property var contactObj: mainItem.chat ? UtilsCpp.findFriendByAddress(mainItem.chat.core.peerAddress) : null + property var contactObj: mainItem.chat ? UtilsCpp.findFriendByAddress(mainItem.chat?.core.peerAddress) : null contact: contactObj?.value || null + displayNameVal: contact ? "" : mainItem.chat.core.avatarUri Layout.preferredWidth: Math.round(45 * DefaultStyle.dp) Layout.preferredHeight: Math.round(45 * DefaultStyle.dp) } @@ -38,6 +43,7 @@ RowLayout { font { pixelSize: Typography.h4.pixelSize weight: Math.round(400 * DefaultStyle.dp) + capitalization: Font.Capitalize } } }, @@ -51,7 +57,7 @@ RowLayout { } BigButton { style: ButtonStyle.noBackground - icon.source: AppIcons.camera + icon.source: AppIcons.videoCamera } BigButton { style: ButtonStyle.noBackground diff --git a/Linphone/view/Page/Layout/Main/MainLayout.qml b/Linphone/view/Page/Layout/Main/MainLayout.qml index 1f3c0e368..6742c4597 100644 --- a/Linphone/view/Page/Layout/Main/MainLayout.qml +++ b/Linphone/view/Page/Layout/Main/MainLayout.qml @@ -26,6 +26,7 @@ Item { signal openCallHistory signal openNumPadRequest signal displayContactRequested(string contactAddress) + signal displayChatRequested(string contactAddress) signal createContactRequested(string name, string address) signal accountRemoved @@ -41,6 +42,10 @@ Item { tabbar.currentIndex = 1 mainItem.displayContactRequested(contactAddress) } + function displayChatPage(contactAddress) { + tabbar.currentIndex = 2 + mainItem.displayChatRequested(contactAddress) + } function createContact(name, address) { tabbar.currentIndex = 1 @@ -616,7 +621,17 @@ Item { } } } - ChatPage{} + ChatPage { + id: chatPage + Connections { + target: mainItem + function onDisplayChatRequested(contactAddress) { + console.log("display chat requested, open with address", contactAddress) + chatPage.remoteAddress = "" + chatPage.remoteAddress = contactAddress + } + } + } MeetingPage {} } } diff --git a/Linphone/view/Page/Main/Call/CallPage.qml b/Linphone/view/Page/Main/Call/CallPage.qml index c00547fc4..4e056729b 100644 --- a/Linphone/view/Page/Main/Call/CallPage.qml +++ b/Linphone/view/Page/Main/Call/CallPage.qml @@ -532,12 +532,9 @@ AbstractMainPage { onClicked: { detailOptions.close() if (contactDetail.isLocalFriend) - mainWindow.displayContactPage( - contactDetail.contactAddress) + mainWindow.displayContactPage(contactDetail.contactAddress) else - mainItem.createContactRequested( - contactDetail.contactName, - contactDetail.contactAddress) + mainItem.createContactRequested(contactDetail.contactName, contactDetail.contactAddress) } } IconLabelButton { diff --git a/Linphone/view/Page/Main/Call/CallSettingsPanel.qml b/Linphone/view/Page/Main/Call/CallSettingsPanel.qml index 004ba50dd..c480ddf67 100644 --- a/Linphone/view/Page/Main/Call/CallSettingsPanel.qml +++ b/Linphone/view/Page/Main/Call/CallSettingsPanel.qml @@ -32,7 +32,7 @@ Control.Page { header: Control.Control { id: pageHeader width: mainItem.width - height: Math.round(56 * DefaultStyle.dp) + height: Math.round(67 * DefaultStyle.dp) leftPadding: Math.round(10 * DefaultStyle.dp) rightPadding: Math.round(10 * DefaultStyle.dp) background: Rectangle { diff --git a/Linphone/view/Page/Main/Chat/ChatPage.qml b/Linphone/view/Page/Main/Chat/ChatPage.qml index 85dcb0fd1..5fc8897a9 100644 --- a/Linphone/view/Page/Main/Chat/ChatPage.qml +++ b/Linphone/view/Page/Main/Chat/ChatPage.qml @@ -18,6 +18,12 @@ AbstractMainPage { property var selectedChatGui + property string remoteAddress + onRemoteAddressChanged: console.log("ChatPage : remote address changed :", remoteAddress) + property var remoteChatObj: UtilsCpp.getChatForAddress(remoteAddress) + property ChatGui remoteChat: remoteChatObj ? remoteChatObj.value : null + onRemoteChatChanged: if (remoteChat) selectedChatGui = remoteChat + onSelectedChatGuiChanged: { if (selectedChatGui) rightPanelStackView.replace(currentChatComp, @@ -151,6 +157,11 @@ AbstractMainPage { onCountChanged: { mainItem.selectedChatGui = model.getAt(currentIndex) } + + Connections { + target: mainItem + onSelectedChatGuiChanged: chatListView.selectChat(mainItem.selectedChatGui) + } } } ScrollBar { diff --git a/Linphone/view/Page/Main/Contact/ContactPage.qml b/Linphone/view/Page/Main/Contact/ContactPage.qml index 73c580597..31af1feed 100644 --- a/Linphone/view/Page/Main/Contact/ContactPage.qml +++ b/Linphone/view/Page/Main/Contact/ContactPage.qml @@ -411,9 +411,7 @@ FriendGui{ height: Math.round(56 * DefaultStyle.dp) button.icon.width: Math.round(24 * DefaultStyle.dp) button.icon.height: Math.round(24 * DefaultStyle.dp) - button.onClicked: mainWindow.startCallWithContact( - contactDetail.contact, - false, mainItem) + button.onClicked: mainWindow.startCallWithContact(contactDetail.contact, false, mainItem) } LabelButton { button.icon.source: AppIcons.chatTeardropText @@ -424,8 +422,10 @@ FriendGui{ height: Math.round(56 * DefaultStyle.dp) button.icon.width: Math.round(24 * DefaultStyle.dp) button.icon.height: Math.round(24 * DefaultStyle.dp) - button.onClicked: console.debug( - "[ContactLayout.qml] TODO : open conversation") + button.onClicked: { + console.debug("[ContactLayout.qml] Open conversation") + mainWindow.displayChatPage(contactDetail.contact.core.defaultAddress) + } } LabelButton { visible: SettingsCpp.videoEnabled @@ -436,9 +436,7 @@ FriendGui{ height: Math.round(56 * DefaultStyle.dp) button.icon.width: Math.round(24 * DefaultStyle.dp) button.icon.height: Math.round(24 * DefaultStyle.dp) - button.onClicked: mainWindow.startCallWithContact( - contactDetail.contact, - true, mainItem) + button.onClicked: mainWindow.startCallWithContact(contactDetail.contact, true, mainItem) } } bannerContent: [ @@ -636,7 +634,7 @@ FriendGui{ EffectImage { Layout.preferredWidth: Math.round(24 * DefaultStyle.dp) Layout.preferredHeight: Math.round(24 * DefaultStyle.dp) - source: AppIcons.shareNetwork + imageSource: AppIcons.shareNetwork colorizationColor: DefaultStyle.main2_600 } Text { diff --git a/Linphone/view/Page/Window/Call/CallsWindow.qml b/Linphone/view/Page/Window/Call/CallsWindow.qml index 87bc6a159..ca0175e30 100644 --- a/Linphone/view/Page/Window/Call/CallsWindow.qml +++ b/Linphone/view/Page/Window/Call/CallsWindow.qml @@ -620,6 +620,19 @@ AbstractWindow { contentStackView.initialItem: callListPanel headerValidateButtonText: qsTr("add") + Binding on topPadding { + when: rightPanel.contentStackView.currentItem.objectName === "chatPanel" + value: 0 + } + Binding on leftPadding { + when: rightPanel.contentStackView.currentItem.objectName === "chatPanel" + value: 0 + } + Binding on rightPadding { + when: rightPanel.contentStackView.currentItem.objectName == "chatPanel" + value: 0 + } + Item { id: numericPadContainer anchors.bottom: parent.bottom @@ -836,6 +849,28 @@ AbstractWindow { } } } + Component { + id: chatPanel + Item { + id: chatPanelContent + objectName: "chatPanel" + Control.StackView.onActivated: { + rightPanel.customHeaderButtons = chatView.callHeaderContent + rightPanel.headerTitleText = "" + } + Keys.onEscapePressed: event => { + rightPanel.visible = false + event.accepted = true + } + SelectedChatView { + id: chatView + anchors.fill: parent + call: mainWindow.call + property var chatObj: UtilsCpp.getCurrentCallChat(mainWindow.call) + chat: chatObj ? chatObj.value : null + } + } + } Component { id: settingsPanel Item { @@ -1313,6 +1348,23 @@ AbstractWindow { } } } + CheckableButton { + iconUrl: AppIcons.chatTeardropText + //: Open chat… + ToolTip.text: qsTr("call_open_chat_hint") + Layout.preferredWidth: Math.round(55 * DefaultStyle.dp) + Layout.preferredHeight: Math.round(55 * DefaultStyle.dp) + icon.width: Math.round(32 * DefaultStyle.dp) + icon.height: Math.round(32 * DefaultStyle.dp) + onCheckedChanged: { + if (checked) { + rightPanel.visible = true + rightPanel.replace(chatPanel) + } else { + rightPanel.visible = false + } + } + } CheckableButton { visible: false checkable: false diff --git a/Linphone/view/Page/Window/Main/MainWindow.qml b/Linphone/view/Page/Window/Main/MainWindow.qml index 1825420d4..a5b9265a2 100644 --- a/Linphone/view/Page/Window/Main/MainWindow.qml +++ b/Linphone/view/Page/Window/Main/MainWindow.qml @@ -56,6 +56,10 @@ AbstractWindow { openMainPage() mainWindowStackView.currentItem.displayContactPage(contactAddress) } + function displayChatPage(contactAddress) { + openMainPage() + mainWindowStackView.currentItem.displayChatPage(contactAddress) + } function transferCallSucceed() { openMainPage() //: "Appel transféré" diff --git a/Linphone/view/Style/buttonStyle.js b/Linphone/view/Style/buttonStyle.js index 0f24865b0..a46df1a8f 100644 --- a/Linphone/view/Style/buttonStyle.js +++ b/Linphone/view/Style/buttonStyle.js @@ -121,7 +121,8 @@ }, image: { normal: Linphone.DefaultStyle.grey_0, - pressed: Linphone.DefaultStyle.grey_0 + pressed: Linphone.DefaultStyle.grey_0, + checked: Linphone.DefaultStyle.grey_0 } }