diff --git a/linphone-app/CMakeLists.txt b/linphone-app/CMakeLists.txt index 8d27697a4..322749cf3 100644 --- a/linphone-app/CMakeLists.txt +++ b/linphone-app/CMakeLists.txt @@ -122,8 +122,9 @@ set(SOURCES src/components/calls/CallsListProxyModel.cpp src/components/camera/Camera.cpp src/components/camera/CameraPreview.cpp - src/components/chat/ChatModel.cpp - src/components/chat/ChatProxyModel.cpp + src/components/chat-room/ChatRoomModel.cpp + src/components/chat-room/ChatRoomListModel.cpp + src/components/chat-room/ChatRoomProxyModel.cpp src/components/codecs/AbstractCodecsModel.cpp src/components/codecs/AudioCodecsModel.cpp src/components/codecs/VideoCodecsModel.cpp @@ -153,6 +154,7 @@ set(SOURCES src/components/other/colors/Colors.cpp src/components/other/text-to-speech/TextToSpeech.cpp src/components/other/units/Units.cpp + src/components/participant/ParticipantModel.cpp src/components/presence/OwnPresenceModel.cpp src/components/presence/Presence.cpp src/components/search/SearchHandler.cpp @@ -198,8 +200,9 @@ set(HEADERS src/components/calls/CallsListProxyModel.hpp src/components/camera/Camera.hpp src/components/camera/CameraPreview.hpp - src/components/chat/ChatModel.hpp - src/components/chat/ChatProxyModel.hpp + src/components/chat-room/ChatRoomModel.hpp + src/components/chat-room/ChatRoomListModel.hpp + src/components/chat-room/ChatRoomProxyModel.hpp src/components/codecs/AbstractCodecsModel.hpp src/components/codecs/AudioCodecsModel.hpp src/components/codecs/VideoCodecsModel.hpp @@ -231,6 +234,7 @@ set(HEADERS src/components/other/desktop-tools/DesktopTools.hpp src/components/other/text-to-speech/TextToSpeech.hpp src/components/other/units/Units.hpp + src/components/participant/ParticipantModel.hpp src/components/presence/OwnPresenceModel.hpp src/components/presence/Presence.hpp src/components/search/SearchHandler.hpp diff --git a/linphone-app/resources.qrc b/linphone-app/resources.qrc index a65070a06..aca2f24d0 100644 --- a/linphone-app/resources.qrc +++ b/linphone-app/resources.qrc @@ -432,6 +432,7 @@ ui/views/App/Main/Dialogs/AuthenticationRequest.qml ui/views/App/Main/Dialogs/ManageAccount.js ui/views/App/Main/Dialogs/ManageAccounts.qml + ui/views/App/Main/Dialogs/ManageChatRoom.qml ui/views/App/Main/Home.qml ui/views/App/Main/HistoryView.qml ui/views/App/Main/HistoryView.js diff --git a/linphone-app/src/app/App.cpp b/linphone-app/src/app/App.cpp index 01649ce7d..57c589499 100644 --- a/linphone-app/src/app/App.cpp +++ b/linphone-app/src/app/App.cpp @@ -582,17 +582,17 @@ void App::registerTypes () { qInfo() << QStringLiteral("Registering types..."); qRegisterMetaType>(); - qRegisterMetaType(); + qRegisterMetaType(); qRegisterMetaType>(); qRegisterMetaType > >(); - qRegisterMetaType>(); + qRegisterMetaType>(); registerType("AssistantModel"); registerType("AuthenticationNotifier"); registerType("CallsListProxyModel"); registerType("Camera"); registerType("CameraPreview"); - registerType("ChatProxyModel"); + registerType("ChatRoomProxyModel"); registerType("ConferenceHelperModel"); registerType("ConferenceModel"); registerType("ContactsListProxyModel"); @@ -616,7 +616,7 @@ void App::registerTypes () { registerSingletonType("VideoCodecsModel"); registerUncreatableType("CallModel"); - registerUncreatableType("ChatModel"); + registerUncreatableType("ChatRoomModel"); registerUncreatableType("ConferenceAddModel"); registerUncreatableType("ContactModel"); registerUncreatableType("ContactsImporterModel"); diff --git a/linphone-app/src/app/logger/Logger.cpp b/linphone-app/src/app/logger/Logger.cpp index 8080e3910..f66363d14 100644 --- a/linphone-app/src/app/logger/Logger.cpp +++ b/linphone-app/src/app/logger/Logger.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include "config.h" @@ -192,7 +193,7 @@ void Logger::enable (bool status) { void Logger::init (const shared_ptr &config) { if (mInstance) return; - + QLoggingCategory::setFilterRules("qt.qml.connections.warning=false"); const QString folder = SettingsModel::getLogsFolder(config); Q_ASSERT(!folder.isEmpty()); diff --git a/linphone-app/src/components/Components.hpp b/linphone-app/src/components/Components.hpp index 60cc7c5fc..f17758b70 100644 --- a/linphone-app/src/components/Components.hpp +++ b/linphone-app/src/components/Components.hpp @@ -28,7 +28,7 @@ #include "calls/CallsListProxyModel.hpp" #include "camera/Camera.hpp" #include "camera/CameraPreview.hpp" -#include "chat/ChatProxyModel.hpp" +#include "chat-room/ChatRoomProxyModel.hpp" #include "codecs/AudioCodecsModel.hpp" #include "codecs/VideoCodecsModel.hpp" #include "conference/ConferenceAddModel.hpp" diff --git a/linphone-app/src/components/call/CallModel.cpp b/linphone-app/src/components/call/CallModel.cpp index edbd923e2..9daf94b5b 100644 --- a/linphone-app/src/components/call/CallModel.cpp +++ b/linphone-app/src/components/call/CallModel.cpp @@ -26,6 +26,8 @@ #include "app/App.hpp" #include "components/calls/CallsListModel.hpp" +#include "components/contact/ContactModel.hpp" +#include "components/contacts/ContactsListModel.hpp" #include "components/core/CoreHandlers.hpp" #include "components/core/CoreManager.hpp" #include "components/notifier/Notifier.hpp" @@ -112,6 +114,13 @@ QString CallModel::getFullLocalAddress () const { } // ----------------------------------------------------------------------------- +ContactModel *CallModel::getContactModel() const{ + auto contact = CoreManager::getInstance()->getContactsListModel()->findContactModelFromSipAddress(QString::fromStdString(mCall->getRemoteAddress()->asString())); + return contact; +} + + +// ----------------------------------------------------------------------------- void CallModel::setRecordFile (const shared_ptr &callParams) { callParams->setRecordFile(Utils::appStringToCoreString( CoreManager::getInstance()->getSettingsModel()->getSavedCallsFolder() diff --git a/linphone-app/src/components/call/CallModel.hpp b/linphone-app/src/components/call/CallModel.hpp index 71ccbf486..92b2ead41 100644 --- a/linphone-app/src/components/call/CallModel.hpp +++ b/linphone-app/src/components/call/CallModel.hpp @@ -26,6 +26,7 @@ #include "../search/SearchHandler.hpp" // ============================================================================= +class ContactModel; class CallModel : public QObject { Q_OBJECT; @@ -34,6 +35,12 @@ class CallModel : public QObject { Q_PROPERTY(QString localAddress READ getLocalAddress CONSTANT); Q_PROPERTY(QString fullPeerAddress READ getFullPeerAddress NOTIFY fullPeerAddressChanged); Q_PROPERTY(QString fullLocalAddress READ getFullLocalAddress CONSTANT); + + Q_PROPERTY(ContactModel *contact READ getContactModel CONSTANT )/* + Q_PROPERTY(QString sipAddress READ getFullPeerAddress NOTIFY fullPeerAddressChanged) + Q_PROPERTY(QString username READ getUsername NOTIFY usernameChanged) + Q_PROPERTY(QString avatar READ getAvatar NOTIFY avatarChanged) + Q_PROPERTY(int presenceStatus READ getPresenceStatus NOTIFY presenceStatusChanged)*/ Q_PROPERTY(CallStatus status READ getStatus NOTIFY statusChanged); Q_PROPERTY(QString callError READ getCallError NOTIFY callErrorChanged); @@ -98,6 +105,8 @@ public: QString getLocalAddress () const; QString getFullPeerAddress () const; QString getFullLocalAddress () const; + + ContactModel *getContactModel() const; bool isInConference () const { return mIsInConference; diff --git a/linphone-app/src/components/calls/CallsListModel.cpp b/linphone-app/src/components/calls/CallsListModel.cpp index 7d0f789c1..91da689da 100644 --- a/linphone-app/src/components/calls/CallsListModel.cpp +++ b/linphone-app/src/components/calls/CallsListModel.cpp @@ -183,6 +183,29 @@ bool CallsListModel::launchSecureChat (const QString &sipAddress) const { */ return false; } + +bool CallsListModel::createSecureChat (const QString& subject, const QString &participantAddress) const{ + shared_ptr core = CoreManager::getInstance()->getCore(); + shared_ptr address = core->interpretUrl(Utils::appStringToCoreString(participantAddress)); + if (!address) + return false; + + std::shared_ptr params = core->createDefaultChatRoomParams(); + std::list > participants; + std::shared_ptr localAddress; + participants.push_back(address); + auto proxy = core->getDefaultProxyConfig(); + params->enableEncryption(true); + + params->setSubject(subject.toStdString()); + params->setBackend(linphone::ChatRoomBackend::FlexisipChat); + params->setEncryptionBackend(linphone::ChatRoomEncryptionBackend::Lime); + params->enableGroup(true); + + std::shared_ptr chatRoom = core->createChatRoom(params, localAddress, participants); + return chatRoom != nullptr; +} + // ----------------------------------------------------------------------------- int CallsListModel::getRunningCallsNumber () const { diff --git a/linphone-app/src/components/calls/CallsListModel.hpp b/linphone-app/src/components/calls/CallsListModel.hpp index 0edb7193c..7a978dfcf 100644 --- a/linphone-app/src/components/calls/CallsListModel.hpp +++ b/linphone-app/src/components/calls/CallsListModel.hpp @@ -45,6 +45,7 @@ public: Q_INVOKABLE void launchAudioCall (const QString &sipAddress, const QHash &headers = {}) const; Q_INVOKABLE void launchVideoCall (const QString &sipAddress) const; Q_INVOKABLE bool launchSecureChat (const QString &sipAddress) const; + Q_INVOKABLE bool createSecureChat (const QString& subject, const QString &participantAddress) const; Q_INVOKABLE int getRunningCallsNumber () const; diff --git a/linphone-app/src/components/chat-room/ChatRoomListModel.cpp b/linphone-app/src/components/chat-room/ChatRoomListModel.cpp new file mode 100644 index 000000000..ee74d4ba0 --- /dev/null +++ b/linphone-app/src/components/chat-room/ChatRoomListModel.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2021 Belledonne Communications SARL. + * + * This file is part of linphone-desktop + * (see https://www.linphone.org). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include + +#include "app/App.hpp" +#include "components/conference/ConferenceAddModel.hpp" +#include "components/conference/ConferenceHelperModel.hpp" +#include "components/core/CoreHandlers.hpp" +#include "components/core/CoreManager.hpp" +#include "components/settings/SettingsModel.hpp" +#include "utils/Utils.hpp" + +#include "ChatRoomListModel.hpp" +#include "ChatRoomModel.hpp" + +// ============================================================================= + +using namespace std; + +namespace { + // Delay before removing call in ms. + constexpr int DelayBeforeRemoveCall = 3000; +} + + +// ----------------------------------------------------------------------------- + +ChatRoomListModel::ChatRoomListModel (QObject *parent) : QAbstractListModel(parent) { +} + +int ChatRoomListModel::rowCount (const QModelIndex &) const { + return mList.count(); +} + +QHash ChatRoomListModel::roleNames () const { + QHash roles; + roles[Qt::DisplayRole] = "$chatRoom"; + return roles; +} + +QVariant ChatRoomListModel::data (const QModelIndex &index, int role) const { + int row = index.row(); + + if (!index.isValid() || row < 0 || row >= mList.count()) + return QVariant(); + + if (role == Qt::DisplayRole) + return QVariant::fromValue(mList[row]); + + return QVariant(); +} + +// ----------------------------------------------------------------------------- + + +bool ChatRoomListModel::removeRow (int row, const QModelIndex &parent) { + return removeRows(row, 1, parent); +} + +bool ChatRoomListModel::removeRows (int row, int count, const QModelIndex &parent) { + int limit = row + count - 1; + + if (row < 0 || count < 0 || limit >= mList.count()) + return false; + + beginRemoveRows(parent, row, limit); + + for (int i = 0; i < count; ++i) + mList.takeAt(row)->deleteLater(); + + endRemoveRows(); + + return true; +} + +// ----------------------------------------------------------------------------- diff --git a/linphone-app/src/components/chat-room/ChatRoomListModel.hpp b/linphone-app/src/components/chat-room/ChatRoomListModel.hpp new file mode 100644 index 000000000..68fe0c747 --- /dev/null +++ b/linphone-app/src/components/chat-room/ChatRoomListModel.hpp @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2021 Belledonne Communications SARL. + * + * This file is part of linphone-desktop + * (see https://www.linphone.org). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef _CHAT_ROOM_LIST_MODEL_H_ +#define _CHAT_ROOM_LIST_MODEL_H_ + +#include +#include + +// ============================================================================= + +class ChatRoomModel; + +class ChatRoomListModel : public QAbstractListModel { + Q_OBJECT; + +public: + ChatRoomListModel (QObject *parent = Q_NULLPTR); + + int rowCount (const QModelIndex &index = QModelIndex()) const override; + + QHash roleNames () const override; + QVariant data (const QModelIndex &index, int role = Qt::DisplayRole) const override; + + +private: + bool removeRow (int row, const QModelIndex &parent = QModelIndex()); + bool removeRows (int row, int count, const QModelIndex &parent = QModelIndex()) override; + + QList mList; + +}; + +#endif // _CHAT_ROOM_LIST_MODEL_H_ diff --git a/linphone-app/src/components/chat/ChatModel.cpp b/linphone-app/src/components/chat-room/ChatRoomModel.cpp similarity index 65% rename from linphone-app/src/components/chat/ChatModel.cpp rename to linphone-app/src/components/chat-room/ChatRoomModel.cpp index 887beb170..99879af52 100644 --- a/linphone-app/src/components/chat/ChatModel.cpp +++ b/linphone-app/src/components/chat-room/ChatRoomModel.cpp @@ -34,14 +34,19 @@ #include "app/App.hpp" #include "app/paths/Paths.hpp" #include "app/providers/ThumbnailProvider.hpp" +#include "components/contact/ContactModel.hpp" +#include "components/contact/VcardModel.hpp" +#include "components/contacts/ContactsListModel.hpp" #include "components/core/CoreHandlers.hpp" #include "components/core/CoreManager.hpp" #include "components/notifier/Notifier.hpp" #include "components/settings/SettingsModel.hpp" +#include "components/participant/ParticipantModel.hpp" +#include "components/presence/Presence.hpp" #include "utils/QExifImageHeader.hpp" #include "utils/Utils.hpp" -#include "ChatModel.hpp" +#include "ChatRoomModel.hpp" // ============================================================================= @@ -204,9 +209,9 @@ static inline void fillMessageEntry (QVariantMap &dest, const shared_ptrgetState(); if (state == linphone::ChatMessage::State::InProgress) - dest["status"] = ChatModel::MessageStatusNotDelivered; + dest["status"] = ChatRoomModel::MessageStatusNotDelivered; else - dest["status"] = static_cast(message->getState()); + dest["status"] = static_cast(message->getState()); shared_ptr content = message->getFileTransferInformation(); if (content) { @@ -220,39 +225,39 @@ static inline void fillMessageEntry (QVariantMap &dest, const shared_ptr &callLog) { - dest["type"] = ChatModel::CallEntry; + dest["type"] = ChatRoomModel::CallEntry; dest["timestamp"] = QDateTime::fromMSecsSinceEpoch(callLog->getStartDate() * 1000); dest["isOutgoing"] = callLog->getDir() == linphone::Call::Dir::Outgoing; - dest["status"] = static_cast(callLog->getStatus()); + dest["status"] = static_cast(callLog->getStatus()); dest["isStart"] = true; } static inline void fillCallEndEntry (QVariantMap &dest, const shared_ptr &callLog) { - dest["type"] = ChatModel::CallEntry; + dest["type"] = ChatRoomModel::CallEntry; dest["timestamp"] = QDateTime::fromMSecsSinceEpoch((callLog->getStartDate() + callLog->getDuration()) * 1000); dest["isOutgoing"] = callLog->getDir() == linphone::Call::Dir::Outgoing; - dest["status"] = static_cast(callLog->getStatus()); + dest["status"] = static_cast(callLog->getStatus()); dest["isStart"] = false; } // ----------------------------------------------------------------------------- -class ChatModel::MessageHandlers : public linphone::ChatMessageListener { - friend class ChatModel; +class ChatRoomModel::MessageHandlers : public linphone::ChatMessageListener { + friend class ChatRoomModel; public: - MessageHandlers (ChatModel *chatModel) : mChatModel(chatModel) {} + MessageHandlers (ChatRoomModel *ChatRoomModel) : mChatRoomModel(ChatRoomModel) {} private: QList::iterator findMessageEntry (const shared_ptr &message) { - return find_if(mChatModel->mEntries.begin(), mChatModel->mEntries.end(), [&message](const ChatEntryData &entry) { + return find_if(mChatRoomModel->mEntries.begin(), mChatRoomModel->mEntries.end(), [&message](const ChatEntryData &entry) { return entry.second == message; }); } void signalDataChanged (const QList::iterator &it) { - int row = int(distance(mChatModel->mEntries.begin(), it)); - emit mChatModel->dataChanged(mChatModel->index(row, 0), mChatModel->index(row, 0)); + int row = int(distance(mChatRoomModel->mEntries.begin(), it)); + emit mChatRoomModel->dataChanged(mChatRoomModel->index(row, 0), mChatRoomModel->index(row, 0)); } shared_ptr onFileTransferSend ( @@ -271,11 +276,11 @@ private: size_t offset, size_t ) override { - if (!mChatModel) + if (!mChatRoomModel) return; auto it = findMessageEntry(message); - if (it == mChatModel->mEntries.end()) + if (it == mChatRoomModel->mEntries.end()) return; (*it).first["fileOffset"] = quint64(offset); @@ -284,11 +289,11 @@ private: } void onMsgStateChanged (const shared_ptr &message, linphone::ChatMessage::State state) override { - if (!mChatModel) + if (!mChatRoomModel) return; auto it = findMessageEntry(message); - if (it == mChatModel->mEntries.end()) + if (it == mChatRoomModel->mEntries.end()) return; // File message downloaded. @@ -304,12 +309,12 @@ private: signalDataChanged(it); } - ChatModel *mChatModel; + ChatRoomModel *mChatRoomModel; }; // ----------------------------------------------------------------------------- - -ChatModel::ChatModel (const QString &peerAddress, const QString &localAddress, const bool& isSecure) { +/* +ChatRoomModel::ChatRoomModel (const QString &peerAddress, const QString &localAddress, const bool& isSecure) { CoreManager *coreManager = CoreManager::getInstance(); mCoreHandlers = coreManager->getHandlers(); @@ -319,21 +324,10 @@ ChatModel::ChatModel (const QString &peerAddress, const QString &localAddress, c shared_ptr factory(linphone::Factory::get()); std::shared_ptr params = core->createDefaultChatRoomParams(); std::list> participants; - - //params->enableEncryption(isSecure); - //if(isSecure){ - // params->setBackend(linphone::ChatRoomBackend::FlexisipChat); -// params->setEncryptionBackend(linphone::ChatRoomEncryptionBackend::Lime); -// } - + mChatRoom = core->searchChatRoom(params, factory->createAddress(localAddress.toStdString()) , factory->createAddress(peerAddress.toStdString()) , participants); - /* - mChatRoom = core->getChatRoom( - factory->createAddress(peerAddress.toStdString()), - factory->createAddress(localAddress.toStdString()) - );*/ Q_ASSERT(mChatRoom); handleIsComposingChanged(mChatRoom); @@ -358,7 +352,7 @@ ChatModel::ChatModel (const QString &peerAddress, const QString &localAddress, c for (auto &callLog : core->getCallHistory(mChatRoom->getPeerAddress(), mChatRoom->getLocalAddress())) insertCall(callLog); - qInfo() << QStringLiteral("ChatModel (%1, %2) loaded in %3 milliseconds.") + qInfo() << QStringLiteral("ChatRoomModel (%1, %2) loaded in %3 milliseconds.") .arg(peerAddress).arg(localAddress).arg(timer.elapsed()); // Rebind lost handlers @@ -371,15 +365,16 @@ ChatModel::ChatModel (const QString &peerAddress, const QString &localAddress, c } { CoreHandlers *coreHandlers = mCoreHandlers.get(); - QObject::connect(coreHandlers, &CoreHandlers::messageReceived, this, &ChatModel::handleMessageReceived); - QObject::connect(coreHandlers, &CoreHandlers::callStateChanged, this, &ChatModel::handleCallStateChanged); - QObject::connect(coreHandlers, &CoreHandlers::isComposingChanged, this, &ChatModel::handleIsComposingChanged); + QObject::connect(coreHandlers, &CoreHandlers::messageReceived, this, &ChatRoomModel::handleMessageReceived); + QObject::connect(coreHandlers, &CoreHandlers::callStateChanged, this, &ChatRoomModel::handleCallStateChanged); + QObject::connect(coreHandlers, &CoreHandlers::isComposingChanged, this, &ChatRoomModel::handleIsComposingChanged); } if(!mChatRoom) qWarning("TOTO A"); } +*/ -ChatModel::ChatModel (std::shared_ptr chatRoom){ +ChatRoomModel::ChatRoomModel (std::shared_ptr chatRoom){ CoreManager *coreManager = CoreManager::getInstance(); mCoreHandlers = coreManager->getHandlers(); @@ -394,8 +389,12 @@ ChatModel::ChatModel (std::shared_ptr chatRoom){ mChatRoom = chatRoom; Q_ASSERT(mChatRoom); + + setLastUpdateTime(QDateTime::fromMSecsSinceEpoch(chatRoom->getLastUpdateTime())); + setUnreadMessagesCount(chatRoom->getUnreadMessagesCount()); + mMissedCallsCount = 0; - handleIsComposingChanged(mChatRoom); + //handleIsComposingChanged(mChatRoom); // Get messages. mEntries.clear(); @@ -430,30 +429,35 @@ ChatModel::ChatModel (std::shared_ptr chatRoom){ } { CoreHandlers *coreHandlers = mCoreHandlers.get(); - QObject::connect(coreHandlers, &CoreHandlers::messageReceived, this, &ChatModel::handleMessageReceived); - QObject::connect(coreHandlers, &CoreHandlers::callStateChanged, this, &ChatModel::handleCallStateChanged); - QObject::connect(coreHandlers, &CoreHandlers::isComposingChanged, this, &ChatModel::handleIsComposingChanged); + //QObject::connect(coreHandlers, &CoreHandlers::messageReceived, this, &ChatRoomModel::handleMessageReceived); + QObject::connect(coreHandlers, &CoreHandlers::callCreated, this, &ChatRoomModel::handleCallCreated); + QObject::connect(coreHandlers, &CoreHandlers::callStateChanged, this, &ChatRoomModel::handleCallStateChanged); + //QObject::connect(coreHandlers, &CoreHandlers::isComposingChanged, this, &ChatRoomModel::handleIsComposingChanged); + } + if(mChatRoom){ + std::list> participants = mChatRoom->getParticipants(); + for(auto it = participants.begin() ; it != participants.end() ; ++it){ + mParticipants << new ParticipantModel(*it, this); + } } - if(!mChatRoom) - qWarning("TOTO B"); } -ChatModel::~ChatModel () { - mMessageHandlers->mChatModel = nullptr; +ChatRoomModel::~ChatRoomModel () { + mMessageHandlers->mChatRoomModel = nullptr; } -QHash ChatModel::roleNames () const { +QHash ChatRoomModel::roleNames () const { QHash roles; roles[Roles::ChatEntry] = "$chatEntry"; roles[Roles::SectionDate] = "$sectionDate"; return roles; } -int ChatModel::rowCount (const QModelIndex &) const { +int ChatRoomModel::rowCount (const QModelIndex &) const { return mEntries.count(); } -QVariant ChatModel::data (const QModelIndex &index, int role) const { +QVariant ChatRoomModel::data (const QModelIndex &index, int role) const { int row = index.row(); if (!index.isValid() || row < 0 || row >= mEntries.count()) @@ -473,11 +477,11 @@ QVariant ChatModel::data (const QModelIndex &index, int role) const { return QVariant(); } -bool ChatModel::removeRow (int row, const QModelIndex &) { +bool ChatRoomModel::removeRow (int row, const QModelIndex &) { return removeRows(row, 1); } -bool ChatModel::removeRows (int row, int count, const QModelIndex &parent) { +bool ChatRoomModel::removeRows (int row, int count, const QModelIndex &parent) { int limit = row + count - 1; if (row < 0 || count < 0 || limit >= mEntries.count()) @@ -500,46 +504,111 @@ bool ChatModel::removeRows (int row, int count, const QModelIndex &parent) { return true; } -QString ChatModel::getPeerAddress () const { +QString ChatRoomModel::getPeerAddress () const { return Utils::coreStringToAppString( mChatRoom->getPeerAddress()->asStringUriOnly() ); } -QString ChatModel::getLocalAddress () const { +QString ChatRoomModel::getLocalAddress () const { return Utils::coreStringToAppString( mChatRoom->getLocalAddress()->asStringUriOnly() ); } -QString ChatModel::getFullPeerAddress () const { +QString ChatRoomModel::getFullPeerAddress () const { if(!mChatRoom) qWarning("TOTO Z"); return QString::fromStdString(mChatRoom->getPeerAddress()->asString()); } -QString ChatModel::getFullLocalAddress () const { +QString ChatRoomModel::getFullLocalAddress () const { return QString::fromStdString(mChatRoom->getLocalAddress()->asString()); } -void ChatModel::setSipAddresses (const QString &peerAddress, const QString &localAddress, const bool& isSecure) { + +QString ChatRoomModel::getSubject () const { + return QString::fromStdString(mChatRoom->getSubject()); +} + +QString ChatRoomModel::getUsername () const { + std::string username = mChatRoom->getSubject(); + if(username != ""){ + return QString::fromStdString(username); + } + if( mChatRoom->getNbParticipants() == 1){ + auto participants = mChatRoom->getParticipants(); + auto contact = CoreManager::getInstance()->getContactsListModel()->findContactModelFromSipAddress(QString::fromStdString((*participants.begin())->getAddress()->asString())); + if(contact) + return contact->getVcardModel()->getUsername(); + } + username = mChatRoom->getPeerAddress()->getDisplayName(); + if(username != "") + return QString::fromStdString(username); + username = mChatRoom->getPeerAddress()->getUsername(); + if(username != "") + return QString::fromStdString(username); + return QString::fromStdString(mChatRoom->getPeerAddress()->asStringUriOnly()); +} + +QString ChatRoomModel::getAvatar () const { + if( mChatRoom->getNbParticipants() == 1){ + auto participants = mChatRoom->getParticipants(); + auto contact = CoreManager::getInstance()->getContactsListModel()->findContactModelFromSipAddress(QString::fromStdString((*participants.begin())->getAddress()->asString())); + if(contact) + return contact->getVcardModel()->getAvatar(); + } + return ""; +} + +int ChatRoomModel::getPresenceStatus() const { + if( mChatRoom->getNbParticipants() == 1){ + auto participants = mChatRoom->getParticipants(); + auto contact = CoreManager::getInstance()->getContactsListModel()->findContactModelFromSipAddress(QString::fromStdString((*participants.begin())->getAddress()->asString())); + if(contact) + return contact->getPresenceLevel(); + else + return 0; + }else + return 0; + //return Presence::getPresenceLevel(1); + //return mChatRoom->getConsolidatedPresence(); +} + +void ChatRoomModel::setLastUpdateTime(const QDateTime& lastUpdateDate) { + if(mLastUpdateTime != lastUpdateDate ) { + mLastUpdateTime = lastUpdateDate; + emit lastUpdateTimeChanged(); + } +} + +void ChatRoomModel::setUnreadMessagesCount(const int& count){ + if(count != mUnreadMessagesCount){ + mUnreadMessagesCount = count; + emit unreadMessagesCountChanged(); + } +} + +void ChatRoomModel::setMissedCallsCount(const int& count){ + if(count != mMissedCallsCount){ + mMissedCallsCount = count; + emit missedCallsCountChanged(); + } +} + + +void ChatRoomModel::leaveChatRoom (){ + mChatRoom->leave(); + mChatRoom->getCore()->deleteChatRoom(mChatRoom); +} +/* +void ChatRoomModel::setSipAddresses (const QString &peerAddress, const QString &localAddress, const bool& isSecure) { shared_ptr core = CoreManager::getInstance()->getCore(); shared_ptr factory(linphone::Factory::get()); std::shared_ptr params = core->createDefaultChatRoomParams(); std::list> participants; - - //params->enableEncryption(isSecure); - //if(isSecure){ - // params->setBackend(linphone::ChatRoomBackend::FlexisipChat); -// params->setEncryptionBackend(linphone::ChatRoomEncryptionBackend::Lime); -// } - + mChatRoom = core->searchChatRoom(params, factory->createAddress(localAddress.toStdString()) , factory->createAddress(peerAddress.toStdString()) , participants); - /* - mChatRoom = core->getChatRoom( - factory->createAddress(peerAddress.toStdString()), - factory->createAddress(localAddress.toStdString()) - );*/ Q_ASSERT(mChatRoom); handleIsComposingChanged(mChatRoom); @@ -564,26 +633,36 @@ void ChatModel::setSipAddresses (const QString &peerAddress, const QString &loca for (auto &callLog : core->getCallHistory(mChatRoom->getPeerAddress(), mChatRoom->getLocalAddress())) insertCall(callLog); - qInfo() << QStringLiteral("ChatModel (%1, %2) loaded in %3 milliseconds.") + qInfo() << QStringLiteral("ChatRoomModel (%1, %2) loaded in %3 milliseconds.") .arg(peerAddress).arg(localAddress).arg(timer.elapsed()); if(!mChatRoom) qWarning("TOTO C"); } +*/ -bool ChatModel::getIsSecure() const{ +bool ChatRoomModel::getIsSecure() const{ return mChatRoom->getSecurityLevel() == linphone::ChatRoomSecurityLevel::Encrypted || mChatRoom->getSecurityLevel() == linphone::ChatRoomSecurityLevel::Safe; } -bool ChatModel::getIsRemoteComposing () const { +bool ChatRoomModel::getIsRemoteComposing () const { return mIsRemoteComposing; } +//QList ChatRoomModel::getParticipants() const{ +QString ChatRoomModel::getParticipants() const{ + QStringList participants; + for(auto it = mParticipants.begin() ; it != mParticipants.end() ; ++it) + participants << (*it)->getAddress(); + + return participants.join(","); +} + // ----------------------------------------------------------------------------- -void ChatModel::removeEntry (int id) { +void ChatRoomModel::removeEntry (int id) { qInfo() << QStringLiteral("Removing chat entry: %1 of (%2, %3).") .arg(id).arg(getPeerAddress()).arg(getLocalAddress()); @@ -591,7 +670,7 @@ void ChatModel::removeEntry (int id) { qWarning() << QStringLiteral("Unable to remove chat entry: %1").arg(id); } -void ChatModel::removeAllEntries () { +void ChatRoomModel::removeAllEntries () { qInfo() << QStringLiteral("Removing all chat entries of: (%1, %2).") .arg(getPeerAddress()).arg(getLocalAddress()); @@ -610,19 +689,18 @@ void ChatModel::removeAllEntries () { // ----------------------------------------------------------------------------- -void ChatModel::sendMessage (const QString &message) { - shared_ptr _message = mChatRoom->createMessage(""); - _message->getContents().begin()->get()->setStringBuffer(message.toUtf8().toStdString()); +void ChatRoomModel::sendMessage (const QString &message) { + shared_ptr _message = mChatRoom->createMessageFromUtf8(""); + _message->getContents().begin()->get()->setUtf8Text(message.toUtf8().toStdString()); _message->removeListener(mMessageHandlers);// Remove old listener if already exists _message->addListener(mMessageHandlers); - insertMessageAtEnd(_message); _message->send(); emit messageSent(_message); } -void ChatModel::resendMessage (int id) { +void ChatRoomModel::resendMessage (int id) { if (id < 0 || id > mEntries.count()) { qWarning() << QStringLiteral("Entry %1 not exists.").arg(id); return; @@ -652,7 +730,7 @@ void ChatModel::resendMessage (int id) { } } -void ChatModel::sendFileMessage (const QString &path) { +void ChatRoomModel::sendFileMessage (const QString &path) { QFile file(path); if (!file.exists()) return; @@ -682,7 +760,6 @@ void ChatModel::sendFileMessage (const QString &path) { createThumbnail(message); - insertMessageAtEnd(message); message->send(); emit messageSent(message); @@ -690,7 +767,7 @@ void ChatModel::sendFileMessage (const QString &path) { // ----------------------------------------------------------------------------- -void ChatModel::downloadFile (int id) { +void ChatRoomModel::downloadFile (int id) { const ChatEntryData entry = getFileMessageEntry(id); if (!entry.second) return; @@ -734,7 +811,7 @@ void ChatModel::downloadFile (int id) { } } -void ChatModel::openFile (int id, bool showDirectory) { +void ChatRoomModel::openFile (int id, bool showDirectory) { const ChatEntryData entry = getFileMessageEntry(id); if (!entry.second) return; @@ -750,27 +827,29 @@ void ChatModel::openFile (int id, bool showDirectory) { } } -bool ChatModel::fileWasDownloaded (int id) { +bool ChatRoomModel::fileWasDownloaded (int id) { const ChatEntryData entry = getFileMessageEntry(id); return entry.second && ::fileWasDownloaded(static_pointer_cast(entry.second)); } -void ChatModel::compose () { +void ChatRoomModel::compose () { mChatRoom->compose(); } -void ChatModel::resetMessageCount () { +void ChatRoomModel::resetMessageCount () { if (mChatRoom->getUnreadMessagesCount() > 0){ mChatRoom->markAsRead();// Marking as read is only for messages. Not for calls. + setUnreadMessagesCount(mChatRoom->getUnreadMessagesCount()); } + mMissedCallsCount = 0; emit messageCountReset(); } -std::shared_ptr ChatModel::getChatRoom(){ +std::shared_ptr ChatRoomModel::getChatRoom(){ return mChatRoom; } // ----------------------------------------------------------------------------- -const ChatModel::ChatEntryData ChatModel::getFileMessageEntry (int id) { +const ChatRoomModel::ChatEntryData ChatRoomModel::getFileMessageEntry (int id) { if (id < 0 || id > mEntries.count()) { qWarning() << QStringLiteral("Entry %1 not exists.").arg(id); return ChatEntryData(); @@ -793,18 +872,18 @@ const ChatModel::ChatEntryData ChatModel::getFileMessageEntry (int id) { // ----------------------------------------------------------------------------- -void ChatModel::removeEntry (ChatEntryData &entry) { +void ChatRoomModel::removeEntry (ChatEntryData &entry) { int type = entry.first["type"].toInt(); switch (type) { - case ChatModel::MessageEntry: { + case ChatRoomModel::MessageEntry: { shared_ptr message = static_pointer_cast(entry.second); removeFileMessageThumbnail(message); mChatRoom->deleteMessage(message); break; } - case ChatModel::CallEntry: { + case ChatRoomModel::CallEntry: { if (entry.first["status"].toInt() == CallStatusSuccess) { // WARNING: Unable to remove symmetric call here. (start/end) // We are between `beginRemoveRows` and `endRemoveRows`. @@ -829,7 +908,7 @@ void ChatModel::removeEntry (ChatEntryData &entry) { } } -void ChatModel::insertCall (const shared_ptr &callLog) { +void ChatRoomModel::insertCall (const shared_ptr &callLog) { linphone::Call::Status status = callLog->getStatus(); auto insertEntry = [this]( @@ -862,7 +941,7 @@ void ChatModel::insertCall (const shared_ptr &callLog) { } } -void ChatModel::insertMessageAtEnd (const shared_ptr &message) { +void ChatRoomModel::insertMessageAtEnd (const shared_ptr &message) { int row = mEntries.count(); beginInsertRows(QModelIndex(), row, row); @@ -879,27 +958,95 @@ void ChatModel::insertMessageAtEnd (const shared_ptr &mes // ----------------------------------------------------------------------------- -void ChatModel::handleCallStateChanged (const shared_ptr &call, linphone::Call::State state) { - if ( - (state == linphone::Call::State::End || state == linphone::Call::State::Error) && - mChatRoom == CoreManager::getInstance()->getCore()->findChatRoom(call->getRemoteAddress(), mChatRoom->getLocalAddress()) - ) - insertCall(call->getCallLog()); +void ChatRoomModel::handleCallStateChanged (const shared_ptr &call, linphone::Call::State state) { + if (state == linphone::Call::State::End || state == linphone::Call::State::Error){ + shared_ptr core = CoreManager::getInstance()->getCore(); + std::shared_ptr params = core->createDefaultChatRoomParams(); + std::list> participants; + + auto chatRoom = core->searchChatRoom(params, mChatRoom->getLocalAddress() + , call->getRemoteAddress() + , participants); + if( mChatRoom == chatRoom){ + insertCall(call->getCallLog()); + setMissedCallsCount(mMissedCallsCount+1); + } + //mChatRoom == CoreManager::getInstance()->getCore()->findChatRoom(call->getRemoteAddress(), mChatRoom->getLocalAddress()) + } } -void ChatModel::handleIsComposingChanged (const shared_ptr &chatRoom) { - if (mChatRoom == chatRoom) { - bool isRemoteComposing = mChatRoom->isRemoteComposing(); - if (isRemoteComposing != mIsRemoteComposing) { - mIsRemoteComposing = isRemoteComposing; +void ChatRoomModel::handleCallCreated(const shared_ptr &call){ +} +//---------------------------------------------------------- +//------ CHAT ROOM HANDLERS +//---------------------------------------------------------- + +void ChatRoomModel::onIsComposingReceived(const std::shared_ptr & chatRoom, const std::shared_ptr & remoteAddress, bool isComposing){ + if (isComposing != mIsRemoteComposing) { + mIsRemoteComposing = isComposing; emit isRemoteComposingChanged(mIsRemoteComposing); } - } } - -void ChatModel::handleMessageReceived (const shared_ptr &message) { - if (mChatRoom == message->getChatRoom()) { - insertMessageAtEnd(message); - emit messageReceived(message); - } +void ChatRoomModel::onMessageReceived(const std::shared_ptr & chatRoom, const std::shared_ptr & message){ + setUnreadMessagesCount(chatRoom->getUnreadMessagesCount()); + /* + insertMessageAtEnd(message); + emit messageReceived(message);*/ } +void ChatRoomModel::onNewEvent(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){ + qWarning() << "New Event" <<(int) eventLog->getType(); + if( eventLog->getType() == linphone::EventLog::Type::ConferenceCallEnd ){ + setMissedCallsCount(mMissedCallsCount+1); + } + /*auto message = eventLog->getChatMessage(); + if(message){ + insertMessageAtEnd(message); + emit messageReceived(message); + }*/ +} +void ChatRoomModel::onChatMessageReceived(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) { + + auto message = eventLog->getChatMessage(); + if(message){ + insertMessageAtEnd(message); + emit messageReceived(message); + setLastUpdateTime(QDateTime::fromMSecsSinceEpoch(chatRoom->getLastUpdateTime())); + } +} +void ChatRoomModel::onChatMessageSending(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){ + auto message = eventLog->getChatMessage(); + if(message){ + insertMessageAtEnd(message); + emit messageReceived(message); + setLastUpdateTime(QDateTime::fromMSecsSinceEpoch(chatRoom->getLastUpdateTime())); + } +} +void ChatRoomModel::onChatMessageSent(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){ + /*auto message = eventLog->getChatMessage(); + if(message){ + insertMessageAtEnd(message); + emit messageReceived(message); + }*/ +} +void ChatRoomModel::onParticipantAdded(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){} +void ChatRoomModel::onParticipantRemoved(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){} +void ChatRoomModel::onParticipantAdminStatusChanged(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){} +void ChatRoomModel::onStateChanged(const std::shared_ptr & chatRoom, linphone::ChatRoom::State newState){} +void ChatRoomModel::onSecurityEvent(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){} +void ChatRoomModel::onSubjectChanged(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) { + emit subjectChanged(getSubject()); + emit usernameChanged(getUsername()); +} +void ChatRoomModel::onUndecryptableMessageReceived(const std::shared_ptr & chatRoom, const std::shared_ptr & message){} +void ChatRoomModel::onParticipantDeviceAdded(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){} +void ChatRoomModel::onParticipantDeviceRemoved(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){} +void ChatRoomModel::onConferenceJoined(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){} +void ChatRoomModel::onConferenceLeft(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){} +void ChatRoomModel::onEphemeralEvent(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){} +void ChatRoomModel::onEphemeralMessageTimerStarted(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){} +void ChatRoomModel::onEphemeralMessageDeleted(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){} +void ChatRoomModel::onConferenceAddressGeneration(const std::shared_ptr & chatRoom){} +void ChatRoomModel::onParticipantRegistrationSubscriptionRequested(const std::shared_ptr & chatRoom, const std::shared_ptr & participantAddress){} +void ChatRoomModel::onParticipantRegistrationUnsubscriptionRequested(const std::shared_ptr & chatRoom, const std::shared_ptr & participantAddress){} +void ChatRoomModel::onChatMessageShouldBeStored(const std::shared_ptr & chatRoom, const std::shared_ptr & message){} +void ChatRoomModel::onChatMessageParticipantImdnStateChanged(const std::shared_ptr & chatRoom, const std::shared_ptr & message, const std::shared_ptr & state){} diff --git a/linphone-app/src/components/chat-room/ChatRoomModel.hpp b/linphone-app/src/components/chat-room/ChatRoomModel.hpp new file mode 100644 index 000000000..565e142a5 --- /dev/null +++ b/linphone-app/src/components/chat-room/ChatRoomModel.hpp @@ -0,0 +1,242 @@ +/* + * Copyright (c) 2010-2020 Belledonne Communications SARL. + * + * This file is part of linphone-desktop + * (see https://www.linphone.org). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef CHAT_ROOM_MODEL_H_ +#define CHAT_ROOM_MODEL_H_ + +#include +#include +#include + +// ============================================================================= +// Fetch all N messages of a ChatRoom. +// ============================================================================= + +class CoreHandlers; +class ParticipantModel; + +class ChatRoomModel : public QAbstractListModel, public linphone::ChatRoomListener { + class MessageHandlers; + + Q_OBJECT; + +public: + enum Roles { + ChatEntry = Qt::DisplayRole, + SectionDate + }; + + enum EntryType { + GenericEntry, + MessageEntry, + CallEntry + }; + Q_ENUM(EntryType); + + enum CallStatus { + CallStatusDeclined = int(linphone::Call::Status::Declined), + CallStatusMissed = int(linphone::Call::Status::Missed), + CallStatusSuccess = int(linphone::Call::Status::Success), + CallStatusAborted = int(linphone::Call::Status::Aborted), + CallStatusEarlyAborted = int(linphone::Call::Status::EarlyAborted), + CallStatusAcceptedElsewhere = int(linphone::Call::Status::AcceptedElsewhere), + CallStatusDeclinedElsewhere = int(linphone::Call::Status::DeclinedElsewhere) + }; + Q_ENUM(CallStatus); + + enum MessageStatus { + MessageStatusDelivered = int(linphone::ChatMessage::State::Delivered), + MessageStatusDeliveredToUser = int(linphone::ChatMessage::State::DeliveredToUser), + MessageStatusDisplayed = int(linphone::ChatMessage::State::Displayed), + MessageStatusFileTransferDone = int(linphone::ChatMessage::State::FileTransferDone), + MessageStatusFileTransferError = int(linphone::ChatMessage::State::FileTransferError), + MessageStatusFileTransferInProgress = int(linphone::ChatMessage::State::FileTransferInProgress), + MessageStatusIdle = int(linphone::ChatMessage::State::Idle), + MessageStatusInProgress = int(linphone::ChatMessage::State::InProgress), + MessageStatusNotDelivered = int(linphone::ChatMessage::State::NotDelivered) + + }; + Q_ENUM(MessageStatus); + + Q_PROPERTY(QString participants READ getParticipants NOTIFY participantsChanged); + Q_PROPERTY(QString subject READ getSubject NOTIFY subjectChanged) + Q_PROPERTY(QDateTime lastUpdateTime MEMBER mLastUpdateTime WRITE setLastUpdateTime NOTIFY lastUpdateTimeChanged) + Q_PROPERTY(int unreadMessagesCount MEMBER mUnreadMessagesCount WRITE setUnreadMessagesCount NOTIFY unreadMessagesCountChanged) + Q_PROPERTY(int missedCallsCount MEMBER mMissedCallsCount WRITE setMissedCallsCount NOTIFY missedCallsCountChanged) + + Q_PROPERTY(bool isComposing MEMBER mIsRemoteComposing NOTIFY isRemoteComposingChanged) + + + + Q_PROPERTY(QString sipAddress READ getFullPeerAddress NOTIFY fullPeerAddressChanged) + Q_PROPERTY(QString username READ getUsername NOTIFY usernameChanged) + Q_PROPERTY(QString avatar READ getAvatar NOTIFY avatarChanged) + Q_PROPERTY(int presenceStatus READ getPresenceStatus NOTIFY presenceStatusChanged) + + + + //ChatRoomModel (const QString &peerAddress, const QString &localAddress, const bool& isSecure); + ChatRoomModel (std::shared_ptr chatRoom); + ~ChatRoomModel (); + + int rowCount (const QModelIndex &index = QModelIndex()) const override; + + QHash roleNames () const override; + QVariant data (const QModelIndex &index, int role) const override; + + bool removeRow (int row, const QModelIndex &parent = QModelIndex()); + bool removeRows (int row, int count, const QModelIndex &parent = QModelIndex()) override; + + Q_INVOKABLE QString getPeerAddress () const; + Q_INVOKABLE QString getLocalAddress () const; + Q_INVOKABLE QString getFullPeerAddress () const; + Q_INVOKABLE QString getFullLocalAddress () const; + + QString getSubject () const; + QString getUsername () const; + QString getAvatar () const; + int getPresenceStatus() const; + void setLastUpdateTime(const QDateTime& lastUpdateDate); + + void setUnreadMessagesCount(const int& count); + void setMissedCallsCount(const int& count); + + Q_INVOKABLE void leaveChatRoom (); + + bool getIsSecure() const; + + bool getIsRemoteComposing () const; + + + //Q_INVOKABLE QList getParticipants()const + Q_INVOKABLE QString getParticipants()const; + + + void removeEntry (int id); + void removeAllEntries (); + + void sendMessage (const QString &message); + + void resendMessage (int id); + + void sendFileMessage (const QString &path); + + void downloadFile (int id); + void openFile (int id, bool showDirectory = false); + void openFileDirectory (int id) { + openFile(id, true); + } + + bool fileWasDownloaded (int id); + + void compose (); + + void resetMessageCount (); + + std::shared_ptr getChatRoom(); + QDateTime mLastUpdateTime; + int mUnreadMessagesCount; + int mMissedCallsCount; + + +//-------------------- CHAT ROOM HANDLER + + + virtual void onIsComposingReceived(const std::shared_ptr & chatRoom, const std::shared_ptr & remoteAddress, bool isComposing) override; + virtual void onMessageReceived(const std::shared_ptr & chatRoom, const std::shared_ptr & message) override; + virtual void onNewEvent(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; + virtual void onChatMessageReceived(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; + virtual void onChatMessageSending(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; + virtual void onChatMessageSent(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; + virtual void onParticipantAdded(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; + virtual void onParticipantRemoved(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; + virtual void onParticipantAdminStatusChanged(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; + virtual void onStateChanged(const std::shared_ptr & chatRoom, linphone::ChatRoom::State newState) override; + virtual void onSecurityEvent(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; + virtual void onSubjectChanged(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; + virtual void onUndecryptableMessageReceived(const std::shared_ptr & chatRoom, const std::shared_ptr & message) override; + virtual void onParticipantDeviceAdded(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; + virtual void onParticipantDeviceRemoved(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; + virtual void onConferenceJoined(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; + virtual void onConferenceLeft(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; + virtual void onEphemeralEvent(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; + virtual void onEphemeralMessageTimerStarted(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; + virtual void onEphemeralMessageDeleted(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; + virtual void onConferenceAddressGeneration(const std::shared_ptr & chatRoom) override; + virtual void onParticipantRegistrationSubscriptionRequested(const std::shared_ptr & chatRoom, const std::shared_ptr & participantAddress) override; + virtual void onParticipantRegistrationUnsubscriptionRequested(const std::shared_ptr & chatRoom, const std::shared_ptr & participantAddress) override; + virtual void onChatMessageShouldBeStored(const std::shared_ptr & chatRoom, const std::shared_ptr & message) override; + virtual void onChatMessageParticipantImdnStateChanged(const std::shared_ptr & chatRoom, const std::shared_ptr & message, const std::shared_ptr & state) override; + + +signals: + bool isRemoteComposingChanged (bool status); + + void allEntriesRemoved (); + void lastEntryRemoved (); + + void messageSent (const std::shared_ptr &message); + void messageReceived (const std::shared_ptr &message); + + void messageCountReset (); + + void focused (); + + void fullPeerAddressChanged(); + void participantsChanged(); + void subjectChanged(QString subject); + void usernameChanged(QString username); + void avatarChanged(QString avatar); + void presenceStatusChanged(int presenceStatus); + void lastUpdateTimeChanged(); + void unreadMessagesCountChanged(); + void missedCallsCountChanged(); + +private: + typedef QPair> ChatEntryData; + + //void setSipAddresses (const QString &peerAddress, const QString &localAddress, const bool& isSecure); + + const ChatEntryData getFileMessageEntry (int id); + + void removeEntry (ChatEntryData &entry); + + void insertCall (const std::shared_ptr &callLog); + void insertMessageAtEnd (const std::shared_ptr &message); + + void handleCallStateChanged (const std::shared_ptr &call, linphone::Call::State state); + void handleCallCreated(const std::shared_ptr &call);// Count an event call + //void handleIsComposingChanged (const std::shared_ptr &chatRoom); + //void handleMessageReceived (const std::shared_ptr &message); + + bool mIsRemoteComposing = false; + + mutable QList mEntries; + QList mParticipants; + + std::shared_ptr mCoreHandlers; + std::shared_ptr mMessageHandlers; + + std::shared_ptr mChatRoom; +}; + +Q_DECLARE_METATYPE(std::shared_ptr); + +#endif // CHAT_ROOM_MODEL_H_ diff --git a/linphone-app/src/components/chat/ChatProxyModel.cpp b/linphone-app/src/components/chat-room/ChatRoomProxyModel.cpp similarity index 51% rename from linphone-app/src/components/chat/ChatProxyModel.cpp rename to linphone-app/src/components/chat-room/ChatRoomProxyModel.cpp index 2093873e5..0eadff228 100644 --- a/linphone-app/src/components/chat/ChatProxyModel.cpp +++ b/linphone-app/src/components/chat-room/ChatRoomProxyModel.cpp @@ -23,31 +23,31 @@ #include "app/App.hpp" #include "components/core/CoreManager.hpp" -#include "ChatProxyModel.hpp" +#include "ChatRoomProxyModel.hpp" // ============================================================================= using namespace std; -QString ChatProxyModel::gCachedText; +QString ChatRoomProxyModel::gCachedText; // Fetch the L last filtered chat entries. -class ChatProxyModel::ChatModelFilter : public QSortFilterProxyModel { +class ChatRoomProxyModel::ChatRoomModelFilter : public QSortFilterProxyModel { public: - ChatModelFilter (QObject *parent) : QSortFilterProxyModel(parent) {} + ChatRoomModelFilter (QObject *parent) : QSortFilterProxyModel(parent) {} - ChatModel::EntryType getEntryTypeFilter () { + ChatRoomModel::EntryType getEntryTypeFilter () { return mEntryTypeFilter; } - void setEntryTypeFilter (ChatModel::EntryType type) { + void setEntryTypeFilter (ChatRoomModel::EntryType type) { mEntryTypeFilter = type; invalidate(); } protected: bool filterAcceptsRow (int sourceRow, const QModelIndex &) const override { - if (mEntryTypeFilter == ChatModel::EntryType::GenericEntry) + if (mEntryTypeFilter == ChatRoomModel::EntryType::GenericEntry) return true; QModelIndex index = sourceModel()->index(sourceRow, 0, QModelIndex()); @@ -57,13 +57,13 @@ protected: } private: - ChatModel::EntryType mEntryTypeFilter = ChatModel::EntryType::GenericEntry; + ChatRoomModel::EntryType mEntryTypeFilter = ChatRoomModel::EntryType::GenericEntry; }; // ============================================================================= -ChatProxyModel::ChatProxyModel (QObject *parent) : QSortFilterProxyModel(parent) { - setSourceModel(new ChatModelFilter(this)); +ChatRoomProxyModel::ChatRoomProxyModel (QObject *parent) : QSortFilterProxyModel(parent) { + setSourceModel(new ChatRoomModelFilter(this)); mIsSecure = false; App *app = App::getInstance(); @@ -81,25 +81,25 @@ ChatProxyModel::ChatProxyModel (QObject *parent) : QSortFilterProxyModel(parent) // ----------------------------------------------------------------------------- #define GET_CHAT_MODEL() \ - if (!mChatModel) \ + if (!mChatRoomModel) \ return; \ - mChatModel + mChatRoomModel #define CREATE_PARENT_MODEL_FUNCTION(METHOD) \ - void ChatProxyModel::METHOD () { \ + void ChatRoomProxyModel::METHOD () { \ GET_CHAT_MODEL()->METHOD(); \ } #define CREATE_PARENT_MODEL_FUNCTION_WITH_PARAM(METHOD, ARG_TYPE) \ - void ChatProxyModel::METHOD (ARG_TYPE value) { \ + void ChatRoomProxyModel::METHOD (ARG_TYPE value) { \ GET_CHAT_MODEL()->METHOD(value); \ } #define CREATE_PARENT_MODEL_FUNCTION_WITH_ID(METHOD) \ - void ChatProxyModel::METHOD (int id) { \ + void ChatRoomProxyModel::METHOD (int id) { \ QModelIndex sourceIndex = mapToSource(index(id, 0)); \ GET_CHAT_MODEL()->METHOD( \ - static_cast(sourceModel())->mapToSource(sourceIndex).row() \ + static_cast(sourceModel())->mapToSource(sourceIndex).row() \ ); \ } @@ -120,15 +120,15 @@ CREATE_PARENT_MODEL_FUNCTION_WITH_ID(resendMessage); #undef CREATE_PARENT_MODEL_FUNCTION_WITH_ID -void ChatProxyModel::compose (const QString& text) { - if (mChatModel) - mChatModel->compose(); +void ChatRoomProxyModel::compose (const QString& text) { + if (mChatRoomModel) + mChatRoomModel->compose(); gCachedText = text; } // ----------------------------------------------------------------------------- -void ChatProxyModel::loadMoreEntries () { +void ChatRoomProxyModel::loadMoreEntries () { int count = rowCount(); int parentCount = sourceModel()->rowCount(); @@ -146,116 +146,116 @@ void ChatProxyModel::loadMoreEntries () { } } -void ChatProxyModel::setEntryTypeFilter (ChatModel::EntryType type) { - ChatModelFilter *chatModelFilter = static_cast(sourceModel()); +void ChatRoomProxyModel::setEntryTypeFilter (ChatRoomModel::EntryType type) { + ChatRoomModelFilter *ChatRoomModelFilter = static_cast(sourceModel()); - if (chatModelFilter->getEntryTypeFilter() != type) { - chatModelFilter->setEntryTypeFilter(type); + if (ChatRoomModelFilter->getEntryTypeFilter() != type) { + ChatRoomModelFilter->setEntryTypeFilter(type); emit entryTypeFilterChanged(type); } } // ----------------------------------------------------------------------------- -bool ChatProxyModel::filterAcceptsRow (int sourceRow, const QModelIndex &) const { +bool ChatRoomProxyModel::filterAcceptsRow (int sourceRow, const QModelIndex &) const { return sourceModel()->rowCount() - sourceRow <= mMaxDisplayedEntries; } // ----------------------------------------------------------------------------- -QString ChatProxyModel::getPeerAddress () const { - return mChatModel ? mChatModel->getPeerAddress() : QString(""); +QString ChatRoomProxyModel::getPeerAddress () const { + return mChatRoomModel ? mChatRoomModel->getPeerAddress() : QString(""); } -void ChatProxyModel::setPeerAddress (const QString &peerAddress) { +void ChatRoomProxyModel::setPeerAddress (const QString &peerAddress) { mPeerAddress = peerAddress; //reload(); } -QString ChatProxyModel::getLocalAddress () const { - return mChatModel ? mChatModel->getLocalAddress() : QString(""); +QString ChatRoomProxyModel::getLocalAddress () const { + return mChatRoomModel ? mChatRoomModel->getLocalAddress() : QString(""); } -void ChatProxyModel::setLocalAddress (const QString &localAddress) { +void ChatRoomProxyModel::setLocalAddress (const QString &localAddress) { mLocalAddress = localAddress; //reload(); } -QString ChatProxyModel::getFullPeerAddress () const { - return mChatModel ? mChatModel->getFullPeerAddress() : QString(""); +QString ChatRoomProxyModel::getFullPeerAddress () const { + return mChatRoomModel ? mChatRoomModel->getFullPeerAddress() : QString(""); } -void ChatProxyModel::setFullPeerAddress (const QString &peerAddress) { +void ChatRoomProxyModel::setFullPeerAddress (const QString &peerAddress) { mFullPeerAddress = peerAddress; //reload(); } -QString ChatProxyModel::getFullLocalAddress () const { - return mChatModel ? mChatModel->getFullLocalAddress() : QString(""); +QString ChatRoomProxyModel::getFullLocalAddress () const { + return mChatRoomModel ? mChatRoomModel->getFullLocalAddress() : QString(""); } -void ChatProxyModel::setFullLocalAddress (const QString &localAddress) { +void ChatRoomProxyModel::setFullLocalAddress (const QString &localAddress) { mFullLocalAddress = localAddress; //reload(); } -int ChatProxyModel::getIsSecure () const { - return mChatModel ? mChatModel->getIsSecure() : -1; +int ChatRoomProxyModel::getIsSecure () const { + return mChatRoomModel ? mChatRoomModel->getIsSecure() : -1; } -void ChatProxyModel::setIsSecure (const int &secure) { +void ChatRoomProxyModel::setIsSecure (const int &secure) { mIsSecure = secure; } -bool ChatProxyModel::getIsRemoteComposing () const { - return mChatModel ? mChatModel->getIsRemoteComposing() : false; +bool ChatRoomProxyModel::getIsRemoteComposing () const { + return mChatRoomModel ? mChatRoomModel->getIsRemoteComposing() : false; } -QString ChatProxyModel::getCachedText() const{ +QString ChatRoomProxyModel::getCachedText() const{ return gCachedText; } // ----------------------------------------------------------------------------- -void ChatProxyModel::reload () { +void ChatRoomProxyModel::reload () { mMaxDisplayedEntries = EntriesChunkSize; - if (mChatModel) { - ChatModel *chatModel = mChatModel.get(); - QObject::disconnect(chatModel, &ChatModel::isRemoteComposingChanged, this, &ChatProxyModel::handleIsRemoteComposingChanged); - QObject::disconnect(chatModel, &ChatModel::messageReceived, this, &ChatProxyModel::handleMessageReceived); - QObject::disconnect(chatModel, &ChatModel::messageSent, this, &ChatProxyModel::handleMessageSent); + if (mChatRoomModel) { + ChatRoomModel *ChatRoomModel = mChatRoomModel.get(); + QObject::disconnect(ChatRoomModel, &ChatRoomModel::isRemoteComposingChanged, this, &ChatRoomProxyModel::handleIsRemoteComposingChanged); + QObject::disconnect(ChatRoomModel, &ChatRoomModel::messageReceived, this, &ChatRoomProxyModel::handleMessageReceived); + QObject::disconnect(ChatRoomModel, &ChatRoomModel::messageSent, this, &ChatRoomProxyModel::handleMessageSent); } - //mChatModel = CoreManager::getInstance()->getChatModel(mPeerAddress, mLocalAddress, mIsSecure); + //mChatRoomModel = CoreManager::getInstance()->getChatRoomModel(mPeerAddress, mLocalAddress, mIsSecure); //if(mChatRoom) - mChatModel = CoreManager::getInstance()->getChatModel(mChatRoom); + mChatRoomModel = CoreManager::getInstance()->getChatRoomModel(mChatRoom); - if (mChatModel) { + if (mChatRoomModel) { - ChatModel *chatModel = mChatModel.get(); - QObject::connect(chatModel, &ChatModel::isRemoteComposingChanged, this, &ChatProxyModel::handleIsRemoteComposingChanged); - QObject::connect(chatModel, &ChatModel::messageReceived, this, &ChatProxyModel::handleMessageReceived); - QObject::connect(chatModel, &ChatModel::messageSent, this, &ChatProxyModel::handleMessageSent); + ChatRoomModel *ChatRoomModel = mChatRoomModel.get(); + QObject::connect(ChatRoomModel, &ChatRoomModel::isRemoteComposingChanged, this, &ChatRoomProxyModel::handleIsRemoteComposingChanged); + QObject::connect(ChatRoomModel, &ChatRoomModel::messageReceived, this, &ChatRoomProxyModel::handleMessageReceived); + QObject::connect(ChatRoomModel, &ChatRoomModel::messageSent, this, &ChatRoomProxyModel::handleMessageSent); } - static_cast(sourceModel())->setSourceModel(mChatModel.get()); + static_cast(sourceModel())->setSourceModel(mChatRoomModel.get()); } -void ChatProxyModel::resetMessageCount(){ - if( mChatModel){ - mChatModel->resetMessageCount(); +void ChatRoomProxyModel::resetMessageCount(){ + if( mChatRoomModel){ + mChatRoomModel->resetMessageCount(); } } -std::shared_ptr ChatProxyModel::getChatModel () const{ - return mChatModel; +ChatRoomModel *ChatRoomProxyModel::getChatRoomModel () const{ + return mChatRoomModel.get(); } -void ChatProxyModel::setChatModel (std::shared_ptr chatModel){ - mChatRoom = chatModel->getChatRoom(); +void ChatRoomProxyModel::setChatRoomModel (ChatRoomModel *ChatRoomModel){ + mChatRoom = ChatRoomModel->getChatRoom(); reload(); - emit chatModelChanged(); + emit chatRoomModelChanged(); } // ----------------------------------------------------------------------------- @@ -269,25 +269,25 @@ static inline QWindow *getParentWindow (QObject *object) { return nullptr; } -void ChatProxyModel::handleIsActiveChanged (QWindow *window) { - if (mChatModel && window->isActive() && getParentWindow(this) == window) { - mChatModel->resetMessageCount(); - mChatModel->focused(); +void ChatRoomProxyModel::handleIsActiveChanged (QWindow *window) { + if (mChatRoomModel && window->isActive() && getParentWindow(this) == window) { + mChatRoomModel->resetMessageCount(); + mChatRoomModel->focused(); } } -void ChatProxyModel::handleIsRemoteComposingChanged (bool status) { +void ChatRoomProxyModel::handleIsRemoteComposingChanged (bool status) { emit isRemoteComposingChanged(status); } -void ChatProxyModel::handleMessageReceived (const shared_ptr &) { +void ChatRoomProxyModel::handleMessageReceived (const shared_ptr &) { mMaxDisplayedEntries++; QWindow *window = getParentWindow(this); if (window && window->isActive()) - mChatModel->resetMessageCount(); + mChatRoomModel->resetMessageCount(); } -void ChatProxyModel::handleMessageSent (const shared_ptr &) { +void ChatRoomProxyModel::handleMessageSent (const shared_ptr &) { mMaxDisplayedEntries++; } diff --git a/linphone-app/src/components/chat/ChatProxyModel.hpp b/linphone-app/src/components/chat-room/ChatRoomProxyModel.hpp similarity index 84% rename from linphone-app/src/components/chat/ChatProxyModel.hpp rename to linphone-app/src/components/chat-room/ChatRoomProxyModel.hpp index c3c50bd82..f0e604bb8 100644 --- a/linphone-app/src/components/chat/ChatProxyModel.hpp +++ b/linphone-app/src/components/chat-room/ChatRoomProxyModel.hpp @@ -18,19 +18,19 @@ * along with this program. If not, see . */ -#ifndef CHAT_PROXY_MODEL_H_ -#define CHAT_PROXY_MODEL_H_ +#ifndef CHAT_ROOM_PROXY_MODEL_H_ +#define CHAT_ROOM_PROXY_MODEL_H_ #include -#include "ChatModel.hpp" +#include "ChatRoomModel.hpp" // ============================================================================= class QWindow; -class ChatProxyModel : public QSortFilterProxyModel { - class ChatModelFilter; +class ChatRoomProxyModel : public QSortFilterProxyModel { + class ChatRoomModelFilter; Q_OBJECT; @@ -39,17 +39,17 @@ class ChatProxyModel : public QSortFilterProxyModel { Q_PROPERTY(QString fullPeerAddress READ getFullPeerAddress WRITE setFullPeerAddress NOTIFY fullPeerAddressChanged); Q_PROPERTY(QString fullLocalAddress READ getFullLocalAddress WRITE setFullLocalAddress NOTIFY fullLocalAddressChanged); Q_PROPERTY(int isSecure READ getIsSecure WRITE setIsSecure NOTIFY isSecureChanged); - Q_PROPERTY(std::shared_ptr chatModel READ getChatModel WRITE setChatModel NOTIFY chatModelChanged); + Q_PROPERTY(ChatRoomModel *chatRoomModel READ getChatRoomModel WRITE setChatRoomModel NOTIFY chatRoomModelChanged); //Q_PROPERTY(bool isSecure MEMBER mIsSecure NOTIFY isSecureChanged); Q_PROPERTY(bool isRemoteComposing READ getIsRemoteComposing NOTIFY isRemoteComposingChanged); //Q_PROPERTY(bool isSecure READ getIsSecure NOTIFY isSecureChanged); Q_PROPERTY(QString cachedText READ getCachedText); public: - ChatProxyModel (QObject *parent = Q_NULLPTR); + ChatRoomProxyModel (QObject *parent = Q_NULLPTR); Q_INVOKABLE void loadMoreEntries (); - Q_INVOKABLE void setEntryTypeFilter (ChatModel::EntryType type); + Q_INVOKABLE void setEntryTypeFilter (ChatRoomModel::EntryType type); Q_INVOKABLE void removeEntry (int id); Q_INVOKABLE void removeAllEntries (); @@ -75,11 +75,11 @@ signals: bool isRemoteComposingChanged (bool status); bool isSecureChanged(bool secure); - void chatModelChanged(); + void chatRoomModelChanged(); void moreEntriesLoaded (int n); - void entryTypeFilterChanged (ChatModel::EntryType type); + void entryTypeFilterChanged (ChatRoomModel::EntryType type); protected: bool filterAcceptsRow (int sourceRow, const QModelIndex &sourceParent) const override; @@ -100,8 +100,8 @@ private: int getIsSecure () const; void setIsSecure (const int &secure); - std::shared_ptr getChatModel() const; - void setChatModel (std::shared_ptr chatModel); + ChatRoomModel *getChatRoomModel() const; + void setChatRoomModel (ChatRoomModel *chatRoomModel); bool getIsRemoteComposing () const; @@ -126,9 +126,9 @@ private: std::shared_ptr mChatRoom; - std::shared_ptr mChatModel; + std::shared_ptr mChatRoomModel; static constexpr int EntriesChunkSize = 50; }; -#endif // CHAT_PROXY_MODEL_H_ +#endif // CHAT_ROOM_PROXY_MODEL_H_ diff --git a/linphone-app/src/components/chat/ChatModel.hpp b/linphone-app/src/components/chat/ChatModel.hpp deleted file mode 100644 index 4002ece96..000000000 --- a/linphone-app/src/components/chat/ChatModel.hpp +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Copyright (c) 2010-2020 Belledonne Communications SARL. - * - * This file is part of linphone-desktop - * (see https://www.linphone.org). - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#ifndef CHAT_MODEL_H_ -#define CHAT_MODEL_H_ - -#include -#include - -// ============================================================================= -// Fetch all N messages of a ChatRoom. -// ============================================================================= - -class CoreHandlers; - -class ChatModel : public QAbstractListModel { - class MessageHandlers; - - Q_OBJECT; - -public: - enum Roles { - ChatEntry = Qt::DisplayRole, - SectionDate - }; - - enum EntryType { - GenericEntry, - MessageEntry, - CallEntry - }; - Q_ENUM(EntryType); - - enum CallStatus { - CallStatusDeclined = int(linphone::Call::Status::Declined), - CallStatusMissed = int(linphone::Call::Status::Missed), - CallStatusSuccess = int(linphone::Call::Status::Success), - CallStatusAborted = int(linphone::Call::Status::Aborted), - CallStatusEarlyAborted = int(linphone::Call::Status::EarlyAborted), - CallStatusAcceptedElsewhere = int(linphone::Call::Status::AcceptedElsewhere), - CallStatusDeclinedElsewhere = int(linphone::Call::Status::DeclinedElsewhere) - }; - Q_ENUM(CallStatus); - - enum MessageStatus { - MessageStatusDelivered = int(linphone::ChatMessage::State::Delivered), - MessageStatusDeliveredToUser = int(linphone::ChatMessage::State::DeliveredToUser), - MessageStatusDisplayed = int(linphone::ChatMessage::State::Displayed), - MessageStatusFileTransferDone = int(linphone::ChatMessage::State::FileTransferDone), - MessageStatusFileTransferError = int(linphone::ChatMessage::State::FileTransferError), - MessageStatusFileTransferInProgress = int(linphone::ChatMessage::State::FileTransferInProgress), - MessageStatusIdle = int(linphone::ChatMessage::State::Idle), - MessageStatusInProgress = int(linphone::ChatMessage::State::InProgress), - MessageStatusNotDelivered = int(linphone::ChatMessage::State::NotDelivered) - - }; - Q_ENUM(MessageStatus); - - ChatModel (const QString &peerAddress, const QString &localAddress, const bool& isSecure); - ChatModel (std::shared_ptr chatRoom); - ~ChatModel (); - - int rowCount (const QModelIndex &index = QModelIndex()) const override; - - QHash roleNames () const override; - QVariant data (const QModelIndex &index, int role) const override; - - bool removeRow (int row, const QModelIndex &parent = QModelIndex()); - bool removeRows (int row, int count, const QModelIndex &parent = QModelIndex()) override; - - QString getPeerAddress () const; - QString getLocalAddress () const; - QString getFullPeerAddress () const; - QString getFullLocalAddress () const; - - bool getIsSecure() const; - - bool getIsRemoteComposing () const; - - void removeEntry (int id); - void removeAllEntries (); - - void sendMessage (const QString &message); - - void resendMessage (int id); - - void sendFileMessage (const QString &path); - - void downloadFile (int id); - void openFile (int id, bool showDirectory = false); - void openFileDirectory (int id) { - openFile(id, true); - } - - bool fileWasDownloaded (int id); - - void compose (); - - void resetMessageCount (); - - std::shared_ptr getChatRoom(); - -signals: - bool isRemoteComposingChanged (bool status); - - void allEntriesRemoved (); - void lastEntryRemoved (); - - void messageSent (const std::shared_ptr &message); - void messageReceived (const std::shared_ptr &message); - - void messageCountReset (); - - void focused (); - -private: - typedef QPair> ChatEntryData; - - void setSipAddresses (const QString &peerAddress, const QString &localAddress, const bool& isSecure); - - const ChatEntryData getFileMessageEntry (int id); - - void removeEntry (ChatEntryData &entry); - - void insertCall (const std::shared_ptr &callLog); - void insertMessageAtEnd (const std::shared_ptr &message); - - void handleCallStateChanged (const std::shared_ptr &call, linphone::Call::State state); - void handleIsComposingChanged (const std::shared_ptr &chatRoom); - void handleMessageReceived (const std::shared_ptr &message); - - bool mIsRemoteComposing = false; - - mutable QList mEntries; - - std::shared_ptr mCoreHandlers; - std::shared_ptr mMessageHandlers; - - std::shared_ptr mChatRoom; -}; - -Q_DECLARE_METATYPE(std::shared_ptr); - -#endif // CHAT_MODEL_H_ diff --git a/linphone-app/src/components/contact/ContactModel.hpp b/linphone-app/src/components/contact/ContactModel.hpp index cb74adb64..a93834d63 100644 --- a/linphone-app/src/components/contact/ContactModel.hpp +++ b/linphone-app/src/components/contact/ContactModel.hpp @@ -51,6 +51,7 @@ public: void mergeVcardModel (VcardModel *vcardModel); Q_INVOKABLE VcardModel *cloneVcardModel () const; + Presence::PresenceLevel getPresenceLevel () const; signals: void contactUpdated (); @@ -65,7 +66,7 @@ private: void updateSipAddresses (VcardModel *oldVcardModel); Presence::PresenceStatus getPresenceStatus () const; - Presence::PresenceLevel getPresenceLevel () const; + VcardModel *mVcardModel = nullptr; std::shared_ptr mLinphoneFriend; diff --git a/linphone-app/src/components/core/CoreHandlers.cpp b/linphone-app/src/components/core/CoreHandlers.cpp index e33d67220..74575f75f 100644 --- a/linphone-app/src/components/core/CoreHandlers.cpp +++ b/linphone-app/src/components/core/CoreHandlers.cpp @@ -194,7 +194,7 @@ void CoreHandlers::onMessageReceived ( if ( !app->hasFocus() || - !CoreManager::getInstance()->chatModelExists( + !CoreManager::getInstance()->chatRoomModelExists( Utils::coreStringToAppString(chatRoom->getPeerAddress()->asStringUriOnly()), Utils::coreStringToAppString(chatRoom->getLocalAddress()->asStringUriOnly()), chatRoom->getSecurityLevel() == linphone::ChatRoomSecurityLevel::Encrypted diff --git a/linphone-app/src/components/core/CoreManager.cpp b/linphone-app/src/components/core/CoreManager.cpp index 9d8584fc7..5ed3e8e60 100644 --- a/linphone-app/src/components/core/CoreManager.cpp +++ b/linphone-app/src/components/core/CoreManager.cpp @@ -29,7 +29,8 @@ #include "app/paths/Paths.hpp" #include "components/calls/CallsListModel.hpp" -#include "components/chat/ChatModel.hpp" +#include "components/chat-room/ChatRoomModel.hpp" +#include "components/chat-room/ChatRoomListModel.hpp" #include "components/contact/VcardModel.hpp" #include "components/contacts/ContactsListModel.hpp" #include "components/contacts/ContactsImporterListModel.hpp" @@ -100,6 +101,7 @@ CoreManager::~CoreManager(){ void CoreManager::initCoreManager(){ mCallsListModel = new CallsListModel(this); + mChatRoomListModel = new ChatRoomListModel(this); mContactsListModel = new ContactsListModel(this); mContactsImporterListModel = new ContactsImporterListModel(this); mAccountSettingsModel = new AccountSettingsModel(this); @@ -120,13 +122,14 @@ CoreManager *CoreManager::getInstance (){ return mInstance; } -shared_ptr CoreManager::getChatModel (const QString &peerAddress, const QString &localAddress, const bool& isSecure) { +/* +shared_ptr CoreManager::getChatRoomModel (const QString &peerAddress, const QString &localAddress, const bool& isSecure) { if (peerAddress.isEmpty() || localAddress.isEmpty()) return nullptr; // Create a new chat model. - QPair> chatModelId{isSecure,{ peerAddress, localAddress }}; - if (!mChatModels.contains(chatModelId)) { + QPair> chatRoomModelId{isSecure,{ peerAddress, localAddress }}; + if (!mChatRoomModels.contains(chatRoomModelId)) { if ( !mCore->createAddress(peerAddress.toStdString()) || !mCore->createAddress(localAddress.toStdString()) @@ -136,31 +139,43 @@ shared_ptr CoreManager::getChatModel (const QString &peerAddress, con return nullptr; } - auto deleter = [this, chatModelId](ChatModel *chatModel) { - bool removed = mChatModels.remove(chatModelId); + auto deleter = [this, chatRoomModelId](ChatRoomModel *chatRoomModel) { + bool removed = mChatRoomModels.remove(chatRoomModelId); Q_ASSERT(removed); - delete chatModel; + delete chatRoomModel; }; - shared_ptr chatModel(new ChatModel(peerAddress, localAddress, isSecure), deleter); - mChatModels[chatModelId] = chatModel; + shared_ptr chatRoomModel(new ChatRoomModel(peerAddress, localAddress, isSecure), deleter); + mChatRoomModels[chatRoomModelId] = chatRoomModel; - emit chatModelCreated(chatModel); + emit chatRoomModelCreated(chatRoomModel); - return chatModel; + return chatRoomModel; } // Returns an existing chat model. - shared_ptr chatModel = mChatModels[chatModelId].lock(); - Q_CHECK_PTR(chatModel); - return chatModel; + shared_ptr chatRoomModel = mChatRoomModels[chatRoomModelId].lock(); + Q_CHECK_PTR(chatRoomModel); + return chatRoomModel; +} +*/ + +shared_ptr CoreManager::getChatRoomModel (ChatRoomModel * data) { + if(data){ + for(auto it = mChatRoomModels.begin() ; it != mChatRoomModels.end() ; ++it){ + auto a = it->lock(); + if(a.get() == data) + return a; + } + } + return nullptr; } -shared_ptr CoreManager::getChatModel (std::shared_ptr chatRoom) { +shared_ptr CoreManager::getChatRoomModel (std::shared_ptr chatRoom) { if (!chatRoom) return nullptr; auto pc = chatRoom->getCurrentParams(); - for(auto it = mChatModels.begin() ; it != mChatModels.end() ; ++it) { + for(auto it = mChatRoomModels.begin() ; it != mChatRoomModels.end() ; ++it) { auto a = it->lock(); auto pa = a->getChatRoom()->getCurrentParams(); if( a->getChatRoom()->getConferenceAddress() == chatRoom->getConferenceAddress() @@ -170,32 +185,34 @@ shared_ptr CoreManager::getChatModel (std::shared_ptrencryptionEnabled() == pc->encryptionEnabled() ){ // Returns an existing chat model. - shared_ptr chatModel = a; - Q_CHECK_PTR(chatModel); - return chatModel; + shared_ptr chatRoomModel = a; + Q_CHECK_PTR(chatRoomModel); + return chatRoomModel; } } - QPair> chatModelId{pc->encryptionEnabled(), + QPair> chatRoomModelId{pc->encryptionEnabled(), { QString::fromStdString(chatRoom->getPeerAddress()->asString()) , QString::fromStdString(chatRoom->getLocalAddress()->asString()) }}; - auto deleter = [this, chatModelId](ChatModel *chatModel) { - bool removed = mChatModels.remove(chatModelId); + auto deleter = [this, chatRoomModelId](ChatRoomModel *chatRoomModel) { + bool removed = mChatRoomModels.remove(chatRoomModelId); Q_ASSERT(removed); - chatModel->deleteLater(); + chatRoomModel->deleteLater(); }; - shared_ptr chatModel(new ChatModel(chatRoom), deleter); - mChatModels[chatModelId] = chatModel; + shared_ptr chatRoomModel(new ChatRoomModel(chatRoom), deleter); + chatRoom->addListener(chatRoomModel); + mChatRoomModels[chatRoomModelId] = chatRoomModel; - emit chatModelCreated(chatModel); + emit chatRoomModelCreated(chatRoomModel); - return chatModel; + return chatRoomModel; } -bool CoreManager::chatModelExists (const QString &peerAddress, const QString &localAddress, const bool &isSecure) { - return mChatModels.contains({isSecure, { peerAddress, localAddress}}); + +bool CoreManager::chatRoomModelExists (const QString &peerAddress, const QString &localAddress, const bool &isSecure) { + return mChatRoomModels.contains({isSecure, { peerAddress, localAddress}}); } HistoryModel* CoreManager::getHistoryModel(){ diff --git a/linphone-app/src/components/core/CoreManager.hpp b/linphone-app/src/components/core/CoreManager.hpp index f055e653b..03ffe73da 100644 --- a/linphone-app/src/components/core/CoreManager.hpp +++ b/linphone-app/src/components/core/CoreManager.hpp @@ -33,7 +33,8 @@ class QTimer; class AccountSettingsModel; class CallsListModel; -class ChatModel; +class ChatRoomModel; +class ChatRoomListModel; class ContactsListModel; class ContactsImporterListModel; class CoreHandlers; @@ -67,9 +68,10 @@ public: return mHandlers; } - std::shared_ptr getChatModel (const QString &peerAddress, const QString &localAddress, const bool &isSecure); - std::shared_ptr getChatModel (std::shared_ptr chatRoom); - bool chatModelExists (const QString &sipAddress, const QString &localAddress, const bool &isSecure); + //std::shared_ptr getChatRoomModel (const QString &peerAddress, const QString &localAddress, const bool &isSecure); + std::shared_ptr getChatRoomModel (ChatRoomModel * data);// Get the shared pointer. This can be done becuase of unicity of ChatRoomModel + std::shared_ptr getChatRoomModel (std::shared_ptr chatRoom); + bool chatRoomModelExists (const QString &sipAddress, const QString &localAddress, const bool &isSecure); HistoryModel* getHistoryModel(); @@ -93,6 +95,12 @@ public: Q_CHECK_PTR(mCallsListModel); return mCallsListModel; } + + ChatRoomListModel *getChatRoomListModel () const { + Q_CHECK_PTR(mChatRoomListModel); + return mChatRoomListModel; + } + ContactsListModel *getContactsListModel () const { Q_CHECK_PTR(mContactsListModel); @@ -163,7 +171,7 @@ public slots: signals: void coreManagerInitialized (); - void chatModelCreated (const std::shared_ptr &chatModel); + void chatRoomModelCreated (const std::shared_ptr &chatRoomModel); void historyModelCreated (HistoryModel *historyModel); void logsUploaded (const QString &url); @@ -200,6 +208,7 @@ private: ContactsListModel *mContactsListModel = nullptr; ContactsImporterListModel *mContactsImporterListModel = nullptr; TimelineListModel *mTimelineListModel = nullptr; + ChatRoomListModel *mChatRoomListModel = nullptr; SipAddressesModel *mSipAddressesModel = nullptr; SettingsModel *mSettingsModel = nullptr; @@ -207,7 +216,7 @@ private: EventCountNotifier *mEventCountNotifier = nullptr; - QHash >, std::weak_ptr> mChatModels; + QHash >, std::weak_ptr> mChatRoomModels; HistoryModel * mHistoryModel = nullptr; LdapListModel *mLdapListModel = nullptr; diff --git a/linphone-app/src/components/core/event-count-notifier/AbstractEventCountNotifier.cpp b/linphone-app/src/components/core/event-count-notifier/AbstractEventCountNotifier.cpp index 5a3f5d0ed..e80f7d37f 100644 --- a/linphone-app/src/components/core/event-count-notifier/AbstractEventCountNotifier.cpp +++ b/linphone-app/src/components/core/event-count-notifier/AbstractEventCountNotifier.cpp @@ -22,7 +22,7 @@ #include "components/call/CallModel.hpp" #include "components/calls/CallsListModel.hpp" -#include "components/chat/ChatModel.hpp" +#include "components/chat-room/ChatRoomModel.hpp" #include "components/core/CoreHandlers.hpp" #include "components/core/CoreManager.hpp" #include "components/history/HistoryModel.hpp" @@ -38,8 +38,8 @@ using namespace std; AbstractEventCountNotifier::AbstractEventCountNotifier (QObject *parent) : QObject(parent) { CoreManager *coreManager = CoreManager::getInstance(); QObject::connect( - coreManager, &CoreManager::chatModelCreated, - this, &AbstractEventCountNotifier::handleChatModelCreated + coreManager, &CoreManager::chatRoomModelCreated, + this, &AbstractEventCountNotifier::handleChatRoomModelCreated ); QObject::connect( coreManager, &CoreManager::historyModelCreated, @@ -95,19 +95,19 @@ int AbstractEventCountNotifier::getMissedCallCountFromLocal(const QString &local } // ----------------------------------------------------------------------------- -void AbstractEventCountNotifier::handleChatModelCreated (const shared_ptr &chatModel) { - ChatModel *chatModelPtr = chatModel.get(); +void AbstractEventCountNotifier::handleChatRoomModelCreated (const shared_ptr &chatRoomModel) { + ChatRoomModel *chatRoomModelPtr = chatRoomModel.get(); QObject::connect( - chatModelPtr, &ChatModel::messageCountReset, + chatRoomModelPtr, &ChatRoomModel::messageCountReset, this, &AbstractEventCountNotifier::updateUnreadMessageCount ); QObject::connect( - chatModelPtr, &ChatModel::focused, - this, [this, chatModelPtr]() { handleResetMissedCalls(chatModelPtr); } + chatRoomModelPtr, &ChatRoomModel::focused, + this, [this, chatRoomModelPtr]() { handleResetMissedCalls(chatRoomModelPtr); } ); QObject::connect( - chatModelPtr, &ChatModel::messageCountReset, - this, [this, chatModelPtr]() { handleResetMissedCalls(chatModelPtr); } + chatRoomModelPtr, &ChatRoomModel::messageCountReset, + this, [this, chatRoomModelPtr]() { handleResetMissedCalls(chatRoomModelPtr); } ); } @@ -122,8 +122,8 @@ void AbstractEventCountNotifier::handleResetAllMissedCalls () { } -void AbstractEventCountNotifier::handleResetMissedCalls (ChatModel *chatModel) { - auto it = mMissedCalls.find({ Utils::cleanSipAddress(chatModel->getPeerAddress()), Utils::cleanSipAddress(chatModel->getLocalAddress()) }); +void AbstractEventCountNotifier::handleResetMissedCalls (ChatRoomModel *chatRoomModel) { + auto it = mMissedCalls.find({ Utils::cleanSipAddress(chatRoomModel->getPeerAddress()), Utils::cleanSipAddress(chatRoomModel->getLocalAddress()) }); if (it != mMissedCalls.cend()) { mMissedCalls.erase(it); internalnotifyEventCount(); diff --git a/linphone-app/src/components/core/event-count-notifier/AbstractEventCountNotifier.hpp b/linphone-app/src/components/core/event-count-notifier/AbstractEventCountNotifier.hpp index d5bea9530..03680c97e 100644 --- a/linphone-app/src/components/core/event-count-notifier/AbstractEventCountNotifier.hpp +++ b/linphone-app/src/components/core/event-count-notifier/AbstractEventCountNotifier.hpp @@ -34,7 +34,7 @@ namespace linphone { } class CallModel; -class ChatModel; +class ChatRoomModel; class HistoryModel; class AbstractEventCountNotifier : public QObject { @@ -67,12 +67,12 @@ private: void internalnotifyEventCount (); - void handleChatModelCreated (const std::shared_ptr &chatModel); + void handleChatRoomModelCreated (const std::shared_ptr &chatRoomModel); void handleHistoryModelCreated (HistoryModel *historyModel); void handleResetAllMissedCalls (); - void handleResetMissedCalls (ChatModel *chatModel); + void handleResetMissedCalls (ChatRoomModel *chatRoomModel); void handleCallMissed (CallModel *callModel); QHash mMissedCalls; diff --git a/linphone-app/src/components/notifier/Notifier.cpp b/linphone-app/src/components/notifier/Notifier.cpp index 3537c22b9..312b0c718 100644 --- a/linphone-app/src/components/notifier/Notifier.cpp +++ b/linphone-app/src/components/notifier/Notifier.cpp @@ -29,6 +29,8 @@ #include "app/App.hpp" #include "components/call/CallModel.hpp" #include "components/core/CoreManager.hpp" +#include "components/timeline/TimelineModel.hpp" +#include "components/timeline/TimelineListModel.hpp" #include "utils/Utils.hpp" #include "Notifier.hpp" @@ -267,12 +269,13 @@ void Notifier::notifyReceivedMessage (const shared_ptr &m if(! message->getFileTransferInformation() ){ foreach(auto content, message->getContents()){ if(content->isText()) - txt += content->getStringBuffer().c_str(); + txt += content->getUtf8Text().c_str(); } }else txt = tr("newFileMessage"); map["message"] = txt; shared_ptr chatRoom(message->getChatRoom()); + map["timelineModel"].setValue(CoreManager::getInstance()->getTimelineListModel()->getTimeline(chatRoom, true).get()); map["peerAddress"] = Utils::coreStringToAppString(chatRoom->getPeerAddress()->asStringUriOnly()); map["localAddress"] = Utils::coreStringToAppString(chatRoom->getLocalAddress()->asStringUriOnly()); map["fullPeerAddress"] = QString::fromStdString(chatRoom->getPeerAddress()->asString()); diff --git a/linphone-app/src/components/participant/ParticipantModel.cpp b/linphone-app/src/components/participant/ParticipantModel.cpp new file mode 100644 index 000000000..4f8b6a499 --- /dev/null +++ b/linphone-app/src/components/participant/ParticipantModel.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2021 Belledonne Communications SARL. + * + * This file is part of linphone-desktop + * (see https://www.linphone.org). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +#include "app/App.hpp" + +#include "ParticipantModel.hpp" +#include "utils/Utils.hpp" + +// ============================================================================= + +using namespace std; + +ParticipantModel::ParticipantModel (shared_ptr linphoneParticipant, QObject *parent) : QObject(parent) { + mLinphoneParticipant = linphoneParticipant; +} + +// ----------------------------------------------------------------------------- + +QString ParticipantModel::getAddress() const{ + return Utils::coreStringToAppString(mLinphoneParticipant->getAddress()->asStringUriOnly()); +} + +QDateTime ParticipantModel::getCreationTime() const{ + return QDateTime::fromSecsSinceEpoch(mLinphoneParticipant->getCreationTime()); +} + +//std::list> ParticipantModel::getDevices() const; +bool ParticipantModel::isAdmin() const{ + return mLinphoneParticipant->isAdmin(); +} +bool ParticipantModel::isFocus() const{ + return mLinphoneParticipant->isFocus(); +} +//linphone::ChatRoomSecurityLevel ParticipantModel::getSecurityLevel() const; +//std::shared_ptr ParticipantModel::findDevice(const std::shared_ptr & address) const; diff --git a/linphone-app/src/components/participant/ParticipantModel.hpp b/linphone-app/src/components/participant/ParticipantModel.hpp new file mode 100644 index 000000000..6ebb7f2d0 --- /dev/null +++ b/linphone-app/src/components/participant/ParticipantModel.hpp @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2021 Belledonne Communications SARL. + * + * This file is part of linphone-desktop + * (see https://www.linphone.org). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef PARTICIPANT_MODEL_H_ +#define PARTICIPANT_MODEL_H_ + + +#include +// ============================================================================= +#include +#include +#include + +class ParticipantModel : public QObject { + + Q_OBJECT; + + Q_PROPERTY(QString address READ getAddress CONSTANT); + Q_PROPERTY(QDateTime creationTime READ getCreationTime CONSTANT); + Q_PROPERTY(bool admin READ isAdmin CONSTANT); + Q_PROPERTY(bool focus READ isFocus CONSTANT); + +public: + ParticipantModel (std::shared_ptr linphoneParticipant, QObject *parent = nullptr); + + QString getAddress() const; + QDateTime getCreationTime() const; + //std::list> getDevices() const; + bool isAdmin() const; + bool isFocus() const; + //linphone::ChatRoomSecurityLevel getSecurityLevel() const; + //std::shared_ptr findDevice(const std::shared_ptr & address) const; + +//signals: +// void contactUpdated (); + + +private: + + std::shared_ptr mLinphoneParticipant; +}; + +Q_DECLARE_METATYPE(ParticipantModel *); + +#endif // PARTICIPANT_MODEL_H_ diff --git a/linphone-app/src/components/participant/ParticipantProxyModel.cpp b/linphone-app/src/components/participant/ParticipantProxyModel.cpp new file mode 100644 index 000000000..3edc783f7 --- /dev/null +++ b/linphone-app/src/components/participant/ParticipantProxyModel.cpp @@ -0,0 +1,293 @@ +/* + * Copyright (c) 2010-2020 Belledonne Communications SARL. + * + * This file is part of linphone-desktop + * (see https://www.linphone.org). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +#include "app/App.hpp" +#include "components/core/CoreManager.hpp" + +#include "ChatRoomProxyModel.hpp" + +// ============================================================================= + +using namespace std; + +QString ChatRoomProxyModel::gCachedText; + +// Fetch the L last filtered chat entries. +class ChatRoomProxyModel::ChatRoomModelFilter : public QSortFilterProxyModel { +public: + ChatRoomModelFilter (QObject *parent) : QSortFilterProxyModel(parent) {} + + ChatRoomModel::EntryType getEntryTypeFilter () { + return mEntryTypeFilter; + } + + void setEntryTypeFilter (ChatRoomModel::EntryType type) { + mEntryTypeFilter = type; + invalidate(); + } + +protected: + bool filterAcceptsRow (int sourceRow, const QModelIndex &) const override { + if (mEntryTypeFilter == ChatRoomModel::EntryType::GenericEntry) + return true; + + QModelIndex index = sourceModel()->index(sourceRow, 0, QModelIndex()); + const QVariantMap data = index.data().toMap(); + + return data["type"].toInt() == mEntryTypeFilter; + } + +private: + ChatRoomModel::EntryType mEntryTypeFilter = ChatRoomModel::EntryType::GenericEntry; +}; + +// ============================================================================= + +ChatRoomProxyModel::ChatRoomProxyModel (QObject *parent) : QSortFilterProxyModel(parent) { + setSourceModel(new ChatRoomModelFilter(this)); + mIsSecure = false; + + App *app = App::getInstance(); + QObject::connect(app->getMainWindow(), &QWindow::activeChanged, this, [this]() { + handleIsActiveChanged(App::getInstance()->getMainWindow()); + }); + + QQuickWindow *callsWindow = app->getCallsWindow(); + if (callsWindow) + QObject::connect(callsWindow, &QWindow::activeChanged, this, [this, callsWindow]() { + handleIsActiveChanged(callsWindow); + }); +} + +// ----------------------------------------------------------------------------- + +#define GET_CHAT_MODEL() \ + if (!mChatRoomModel) \ + return; \ + mChatRoomModel + +#define CREATE_PARENT_MODEL_FUNCTION(METHOD) \ + void ChatRoomProxyModel::METHOD () { \ + GET_CHAT_MODEL()->METHOD(); \ + } + +#define CREATE_PARENT_MODEL_FUNCTION_WITH_PARAM(METHOD, ARG_TYPE) \ + void ChatRoomProxyModel::METHOD (ARG_TYPE value) { \ + GET_CHAT_MODEL()->METHOD(value); \ + } + +#define CREATE_PARENT_MODEL_FUNCTION_WITH_ID(METHOD) \ + void ChatRoomProxyModel::METHOD (int id) { \ + QModelIndex sourceIndex = mapToSource(index(id, 0)); \ + GET_CHAT_MODEL()->METHOD( \ + static_cast(sourceModel())->mapToSource(sourceIndex).row() \ + ); \ + } + +CREATE_PARENT_MODEL_FUNCTION(removeAllEntries); + +CREATE_PARENT_MODEL_FUNCTION_WITH_PARAM(sendFileMessage, const QString &); +CREATE_PARENT_MODEL_FUNCTION_WITH_PARAM(sendMessage, const QString &); + +CREATE_PARENT_MODEL_FUNCTION_WITH_ID(downloadFile); +CREATE_PARENT_MODEL_FUNCTION_WITH_ID(openFile); +CREATE_PARENT_MODEL_FUNCTION_WITH_ID(openFileDirectory); +CREATE_PARENT_MODEL_FUNCTION_WITH_ID(removeEntry); +CREATE_PARENT_MODEL_FUNCTION_WITH_ID(resendMessage); + +#undef GET_CHAT_MODEL +#undef CREATE_PARENT_MODEL_FUNCTION +#undef CREATE_PARENT_MODEL_FUNCTION_WITH_PARAM +#undef CREATE_PARENT_MODEL_FUNCTION_WITH_ID + + +void ChatRoomProxyModel::compose (const QString& text) { + if (mChatRoomModel) + mChatRoomModel->compose(); + gCachedText = text; +} + +// ----------------------------------------------------------------------------- + +void ChatRoomProxyModel::loadMoreEntries () { + int count = rowCount(); + int parentCount = sourceModel()->rowCount(); + + if (count < parentCount) { + // Do not increase `mMaxDisplayedEntries` if it's not necessary... + // Limit qml calls. + if (count == mMaxDisplayedEntries) + mMaxDisplayedEntries += EntriesChunkSize; + + invalidateFilter(); + + count = rowCount() - count; + if (count > 0) + emit moreEntriesLoaded(count); + } +} + +void ChatRoomProxyModel::setEntryTypeFilter (ChatRoomModel::EntryType type) { + ChatRoomModelFilter *chatRoomModelFilter = static_cast(sourceModel()); + + if (chatRoomModelFilter->getEntryTypeFilter() != type) { + chatRoomModelFilter->setEntryTypeFilter(type); + emit entryTypeFilterChanged(type); + } +} + +// ----------------------------------------------------------------------------- + +bool ChatRoomProxyModel::filterAcceptsRow (int sourceRow, const QModelIndex &) const { + return sourceModel()->rowCount() - sourceRow <= mMaxDisplayedEntries; +} + +// ----------------------------------------------------------------------------- + +QString ChatRoomProxyModel::getPeerAddress () const { + return mChatRoomModel ? mChatRoomModel->getPeerAddress() : QString(""); +} + +void ChatRoomProxyModel::setPeerAddress (const QString &peerAddress) { + mPeerAddress = peerAddress; + //reload(); +} + +QString ChatRoomProxyModel::getLocalAddress () const { + return mChatRoomModel ? mChatRoomModel->getLocalAddress() : QString(""); +} + +void ChatRoomProxyModel::setLocalAddress (const QString &localAddress) { + mLocalAddress = localAddress; + //reload(); +} + +QString ChatRoomProxyModel::getFullPeerAddress () const { + return mChatRoomModel ? mChatRoomModel->getFullPeerAddress() : QString(""); +} + +void ChatRoomProxyModel::setFullPeerAddress (const QString &peerAddress) { + mFullPeerAddress = peerAddress; + //reload(); +} + +QString ChatRoomProxyModel::getFullLocalAddress () const { + return mChatRoomModel ? mChatRoomModel->getFullLocalAddress() : QString(""); +} + +void ChatRoomProxyModel::setFullLocalAddress (const QString &localAddress) { + mFullLocalAddress = localAddress; + //reload(); +} + +int ChatRoomProxyModel::getIsSecure () const { + return mChatRoomModel ? mChatRoomModel->getIsSecure() : -1; +} + +void ChatRoomProxyModel::setIsSecure (const int &secure) { + mIsSecure = secure; +} + +bool ChatRoomProxyModel::getIsRemoteComposing () const { + return mChatRoomModel ? mChatRoomModel->getIsRemoteComposing() : false; +} + +QString ChatRoomProxyModel::getCachedText() const{ + return gCachedText; +} + +// ----------------------------------------------------------------------------- + +void ChatRoomProxyModel::reload () { + mMaxDisplayedEntries = EntriesChunkSize; + + if (mChatRoomModel) { + ChatRoomModel *chatRoomModel = mChatRoomModel.get(); + QObject::disconnect(chatRoomModel, &ChatRoomModel::isRemoteComposingChanged, this, &ChatRoomProxyModel::handleIsRemoteComposingChanged); + QObject::disconnect(chatRoomModel, &ChatRoomModel::messageReceived, this, &ChatRoomProxyModel::handleMessageReceived); + QObject::disconnect(chatRoomModel, &ChatRoomModel::messageSent, this, &ChatRoomProxyModel::handleMessageSent); + } + + //mChatRoomModel = CoreManager::getInstance()->getChatRoomModel(mPeerAddress, mLocalAddress, mIsSecure); + //if(mChatRoom) + mChatRoomModel = CoreManager::getInstance()->getChatRoomModel(mChatRoom); + + + if (mChatRoomModel) { + + ChatRoomModel *chatRoomModel = mChatRoomModel.get(); + QObject::connect(chatRoomModel, &ChatRoomModel::isRemoteComposingChanged, this, &ChatRoomProxyModel::handleIsRemoteComposingChanged); + QObject::connect(chatRoomModel, &ChatRoomModel::messageReceived, this, &ChatRoomProxyModel::handleMessageReceived); + QObject::connect(chatRoomModel, &ChatRoomModel::messageSent, this, &ChatRoomProxyModel::handleMessageSent); + } + + static_cast(sourceModel())->setSourceModel(mChatRoomModel.get()); +} +void ChatRoomProxyModel::resetMessageCount(){ + if( mChatRoomModel){ + mChatRoomModel->resetMessageCount(); + } +} + +std::shared_ptr ChatRoomProxyModel::getChatRoomModel () const{ + return mChatRoomModel; + +} +void ChatRoomProxyModel::setChatRoomModel (std::shared_ptr chatRoomModel){ + mChatRoom = chatRoomModel->getChatRoom(); + reload(); + emit chatRoomModelChanged(); +} +// ----------------------------------------------------------------------------- + +static inline QWindow *getParentWindow (QObject *object) { + App *app = App::getInstance(); + const QWindow *mainWindow = app->getMainWindow(); + const QWindow *callsWindow = app->getCallsWindow(); + for (QObject *parent = object->parent(); parent; parent = parent->parent()) + if (parent == mainWindow || parent == callsWindow) + return static_cast(parent); + return nullptr; +} + +void ChatRoomProxyModel::handleIsActiveChanged (QWindow *window) { + if (mChatRoomModel && window->isActive() && getParentWindow(this) == window) { + mChatRoomModel->resetMessageCount(); + mChatRoomModel->focused(); + } +} + +void ChatRoomProxyModel::handleIsRemoteComposingChanged (bool status) { + emit isRemoteComposingChanged(status); +} + +void ChatRoomProxyModel::handleMessageReceived (const shared_ptr &) { + mMaxDisplayedEntries++; + + QWindow *window = getParentWindow(this); + if (window && window->isActive()) + mChatRoomModel->resetMessageCount(); +} + +void ChatRoomProxyModel::handleMessageSent (const shared_ptr &) { + mMaxDisplayedEntries++; +} diff --git a/linphone-app/src/components/participant/ParticipantProxyModel.hpp b/linphone-app/src/components/participant/ParticipantProxyModel.hpp new file mode 100644 index 000000000..91ed8fe76 --- /dev/null +++ b/linphone-app/src/components/participant/ParticipantProxyModel.hpp @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2020 Belledonne Communications SARL. + * + * This file is part of linphone-desktop + * (see https://www.linphone.org). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef PARTICIPANT_PROXY_MODEL_H_ +#define PARTICIPANT_PROXY_MODEL_H_ + +#include + +#include "ParticipantModel.hpp" + +// ============================================================================= + +class QWindow; + +class ParticipantProxyModel : public QSortFilterProxyModel { + + Q_OBJECT + + +public: + ParticipantProxyModel (QObject *parent = Q_NULLPTR); + + void reset(); + void update(); + std::shared_ptr getTimeline(std::shared_ptr chatRoom, const bool &create); + + int rowCount (const QModelIndex &index = QModelIndex()) const override; + + QHash roleNames () const override; + QVariant data (const QModelIndex &index, int role = Qt::DisplayRole) const override; + +// Remove a chatroom + Q_INVOKABLE void remove (TimelineModel *importer); + +private: + bool removeRow (int row, const QModelIndex &parent = QModelIndex()); + bool removeRows (int row, int count, const QModelIndex &parent = QModelIndex()) override; + + + + void initTimeline (); + void updateTimelines(); + + QList> mParticipantlines; +}; + +#endif // PARTICIPANT_PROXY_MODEL_H_ diff --git a/linphone-app/src/components/settings/AccountSettingsModel.cpp b/linphone-app/src/components/settings/AccountSettingsModel.cpp index f3473c2ab..164ea0fac 100644 --- a/linphone-app/src/components/settings/AccountSettingsModel.cpp +++ b/linphone-app/src/components/settings/AccountSettingsModel.cpp @@ -137,6 +137,7 @@ QVariantMap AccountSettingsModel::getProxyConfigDescription (const shared_ptr
  • getRoutes().front()); else map["route"] = ""; + map["conferenceUri"] = Utils::coreStringToAppString(proxyConfig->getConferenceFactoryUri()); map["contactParams"] = Utils::coreStringToAppString(proxyConfig->getContactParameters()); map["avpfInterval"] = proxyConfig->getAvpfRrInterval(); map["registerEnabled"] = proxyConfig->registerEnabled(); @@ -245,6 +246,7 @@ bool AccountSettingsModel::addOrUpdateProxyConfig ( proxyConfig->setPublishExpires(data["registrationDuration"].toInt()); proxyConfig->setRoute(Utils::appStringToCoreString(data["route"].toString())); + proxyConfig->setConferenceFactoryUri(Utils::appStringToCoreString(data["conferenceUri"].toString())); proxyConfig->setContactParameters(Utils::appStringToCoreString(data["contactParams"].toString())); proxyConfig->setAvpfRrInterval(uint8_t(data["avpfInterval"].toInt())); proxyConfig->enableRegister(data["registerEnabled"].toBool()); diff --git a/linphone-app/src/components/sip-addresses/SearchSipAddressesModel.cpp b/linphone-app/src/components/sip-addresses/SearchSipAddressesModel.cpp index 74aa5956f..1ef63fc27 100644 --- a/linphone-app/src/components/sip-addresses/SearchSipAddressesModel.cpp +++ b/linphone-app/src/components/sip-addresses/SearchSipAddressesModel.cpp @@ -24,7 +24,7 @@ #include #include "components/call/CallModel.hpp" -#include "components/chat/ChatModel.hpp" +#include "components/chat-room/ChatRoomModel.hpp" #include "components/contact/ContactModel.hpp" #include "components/contact/VcardModel.hpp" #include "components/contacts/ContactsListModel.hpp" diff --git a/linphone-app/src/components/sip-addresses/SipAddressesModel.cpp b/linphone-app/src/components/sip-addresses/SipAddressesModel.cpp index b1eaca2da..353894c61 100644 --- a/linphone-app/src/components/sip-addresses/SipAddressesModel.cpp +++ b/linphone-app/src/components/sip-addresses/SipAddressesModel.cpp @@ -24,7 +24,7 @@ #include #include "components/call/CallModel.hpp" -#include "components/chat/ChatModel.hpp" +#include "components/chat-room/ChatRoomModel.hpp" #include "components/contact/ContactModel.hpp" #include "components/contact/VcardModel.hpp" #include "components/contacts/ContactsListModel.hpp" @@ -59,7 +59,7 @@ SipAddressesModel::SipAddressesModel (QObject *parent) : QAbstractListModel(pare mCoreHandlers = coreManager->getHandlers(); - QObject::connect(coreManager, &CoreManager::chatModelCreated, this, &SipAddressesModel::handleChatModelCreated); + QObject::connect(coreManager, &CoreManager::chatRoomModelCreated, this, &SipAddressesModel::handleChatRoomModelCreated); QObject::connect(coreManager, &CoreManager::historyModelCreated, this, &SipAddressesModel::handleHistoryModelCreated); ContactsListModel *contacts = CoreManager::getInstance()->getContactsListModel(); @@ -264,20 +264,20 @@ bool SipAddressesModel::removeRows (int row, int count, const QModelIndex &paren // ----------------------------------------------------------------------------- -void SipAddressesModel::handleChatModelCreated (const shared_ptr &chatModel) { - ChatModel *ptr = chatModel.get(); +void SipAddressesModel::handleChatRoomModelCreated (const shared_ptr &chatRoomModel) { + ChatRoomModel *ptr = chatRoomModel.get(); - QObject::connect(ptr, &ChatModel::allEntriesRemoved, this, [this, ptr] { + QObject::connect(ptr, &ChatRoomModel::allEntriesRemoved, this, [this, ptr] { handleAllEntriesRemoved(ptr); }); - QObject::connect(ptr, &ChatModel::lastEntryRemoved, this, [this, ptr] { + QObject::connect(ptr, &ChatRoomModel::lastEntryRemoved, this, [this, ptr] { handleLastEntryRemoved(ptr); }); - QObject::connect(ptr, &ChatModel::messageCountReset, this, [this, ptr] { + QObject::connect(ptr, &ChatRoomModel::messageCountReset, this, [this, ptr] { handleMessageCountReset(ptr); }); - QObject::connect(ptr, &ChatModel::messageSent, this, &SipAddressesModel::handleMessageSent); + QObject::connect(ptr, &ChatRoomModel::messageSent, this, &SipAddressesModel::handleMessageSent); } void SipAddressesModel::handleHistoryModelCreated (HistoryModel *historyModel) { @@ -365,12 +365,12 @@ void SipAddressesModel::handlePresenceReceived ( updateObservers(sipAddress, status); } -void SipAddressesModel::handleAllEntriesRemoved (ChatModel *chatModel) { - auto it = mPeerAddressToSipAddressEntry.find(chatModel->getPeerAddress()); +void SipAddressesModel::handleAllEntriesRemoved (ChatRoomModel *chatRoomModel) { + auto it = mPeerAddressToSipAddressEntry.find(chatRoomModel->getPeerAddress()); if (it == mPeerAddressToSipAddressEntry.end()) return; - auto it2 = it->localAddressToConferenceEntry.find(Utils::cleanSipAddress(chatModel->getLocalAddress())); + auto it2 = it->localAddressToConferenceEntry.find(Utils::cleanSipAddress(chatRoomModel->getLocalAddress())); if (it2 == it->localAddressToConferenceEntry.end()) return; it->localAddressToConferenceEntry.erase(it2); @@ -387,22 +387,22 @@ void SipAddressesModel::handleAllEntriesRemoved (ChatModel *chatModel) { emit dataChanged(index(row, 0), index(row, 0)); } -void SipAddressesModel::handleLastEntryRemoved (ChatModel *chatModel) { - auto it = mPeerAddressToSipAddressEntry.find(chatModel->getPeerAddress()); +void SipAddressesModel::handleLastEntryRemoved (ChatRoomModel *chatRoomModel) { + auto it = mPeerAddressToSipAddressEntry.find(chatRoomModel->getPeerAddress()); if (it == mPeerAddressToSipAddressEntry.end()) return; - auto it2 = it->localAddressToConferenceEntry.find(Utils::cleanSipAddress(chatModel->getLocalAddress())); + auto it2 = it->localAddressToConferenceEntry.find(Utils::cleanSipAddress(chatRoomModel->getLocalAddress())); if (it2 == it->localAddressToConferenceEntry.end()) return; int row = mRefs.indexOf(&(*it)); Q_ASSERT(row != -1); - Q_ASSERT(chatModel->rowCount() > 0); - const QVariantMap map = chatModel->data( - chatModel->index(chatModel->rowCount() - 1, 0), - ChatModel::ChatEntry + Q_ASSERT(chatRoomModel->rowCount() > 0); + const QVariantMap map = chatRoomModel->data( + chatRoomModel->index(chatRoomModel->rowCount() - 1, 0), + ChatRoomModel::ChatEntry ).toMap(); // Update the timestamp with the new last chat message timestamp. @@ -419,13 +419,13 @@ void SipAddressesModel::handleAllCallCountReset () { emit dataChanged(index(row, 0), index(row, 0)); } } -void SipAddressesModel::handleMessageCountReset (ChatModel *chatModel) { - const QString &peerAddress = Utils::cleanSipAddress(chatModel->getPeerAddress()); +void SipAddressesModel::handleMessageCountReset (ChatRoomModel *chatRoomModel) { + const QString &peerAddress = Utils::cleanSipAddress(chatRoomModel->getPeerAddress()); auto it = mPeerAddressToSipAddressEntry.find(peerAddress); if (it == mPeerAddressToSipAddressEntry.end()) return; - const QString &localAddress = Utils::cleanSipAddress(chatModel->getLocalAddress()); + const QString &localAddress = Utils::cleanSipAddress(chatRoomModel->getLocalAddress()); auto it2 = it->localAddressToConferenceEntry.find(localAddress); if (it2 == it->localAddressToConferenceEntry.end()) return; diff --git a/linphone-app/src/components/sip-addresses/SipAddressesModel.hpp b/linphone-app/src/components/sip-addresses/SipAddressesModel.hpp index daf774d40..090c88cef 100644 --- a/linphone-app/src/components/sip-addresses/SipAddressesModel.hpp +++ b/linphone-app/src/components/sip-addresses/SipAddressesModel.hpp @@ -30,7 +30,7 @@ class QUrl; -class ChatModel; +class ChatRoomModel; class CoreHandlers; class HistoryModel; @@ -94,7 +94,7 @@ private: // --------------------------------------------------------------------------- - void handleChatModelCreated (const std::shared_ptr &chatModel); + void handleChatRoomModelCreated (const std::shared_ptr &chatRoomModel); void handleHistoryModelCreated (HistoryModel *historyModel) ; void handleContactAdded (ContactModel *contact); @@ -107,10 +107,10 @@ private: void handleCallStateChanged (const std::shared_ptr &call, linphone::Call::State state); void handlePresenceReceived (const QString &sipAddress, const std::shared_ptr &presenceModel); - void handleAllEntriesRemoved (ChatModel *chatModel); - void handleLastEntryRemoved (ChatModel *chatModel); + void handleAllEntriesRemoved (ChatRoomModel *chatRoomModel); + void handleLastEntryRemoved (ChatRoomModel *chatRoomModel); - void handleMessageCountReset (ChatModel *chatModel); + void handleMessageCountReset (ChatRoomModel *chatRoomModel); void handleMessageSent (const std::shared_ptr &message); diff --git a/linphone-app/src/components/timeline/TimelineListModel.cpp b/linphone-app/src/components/timeline/TimelineListModel.cpp index 22c3de205..3e4debdae 100644 --- a/linphone-app/src/components/timeline/TimelineListModel.cpp +++ b/linphone-app/src/components/timeline/TimelineListModel.cpp @@ -32,21 +32,29 @@ // ============================================================================= TimelineListModel::TimelineListModel (QObject *parent) : QAbstractListModel(parent) { - initTimeline(); + //initTimeline(); + mSelectedCount = 0; + updateTimelines (); } // ----------------------------------------------------------------------------- TimelineModel * TimelineListModel::getAt(const int& index){ - return mTimelines[index]; + return mTimelines[index].get(); } void TimelineListModel::reset(){ - initTimeline(); + //initTimeline(); + updateTimelines (); } void TimelineListModel::update(){ - + updateTimelines (); +} + +void TimelineListModel::selectAll(const bool& selected){ + for(auto it = mTimelines.begin() ; it != mTimelines.end() ; ++it) + (*it)->mSelected = selected; } int TimelineListModel::rowCount (const QModelIndex &) const { return mTimelines.count(); @@ -65,7 +73,7 @@ QVariant TimelineListModel::data (const QModelIndex &index, int role) const { return QVariant(); if (role == Qt::DisplayRole) - return QVariant::fromValue(mTimelines[row]); + return QVariant::fromValue(mTimelines[row].get()); return QVariant(); } @@ -88,8 +96,10 @@ bool TimelineListModel::removeRows (int row, int count, const QModelIndex &paren beginRemoveRows(parent, row, limit); - for (int i = 0; i < count; ++i) - delete mTimelines.takeAt(row); + for (int i = 0; i < count; ++i){ + auto timeline = mTimelines.takeAt(row); + timeline->getChatRoomModel()->getChatRoom()->removeListener(timeline); + } endRemoveRows(); @@ -100,11 +110,12 @@ bool TimelineListModel::removeRows (int row, int count, const QModelIndex &paren // ----------------------------------------------------------------------------- void TimelineListModel::initTimeline () { + /* CoreManager *coreManager = CoreManager::getInstance(); auto currentAddress = coreManager->getAccountSettingsModel()->getUsedSipAddress(); std::list> allChatRooms = coreManager->getCore()->getChatRooms(); - QList models; + QList> models; for(auto itAllChatRooms = allChatRooms.begin() ; itAllChatRooms != allChatRooms.end() ; ++itAllChatRooms){ if((*itAllChatRooms)->getMe()->getAddress()->weakEqual(currentAddress)){ models << new TimelineModel(*itAllChatRooms); @@ -115,7 +126,7 @@ void TimelineListModel::initTimeline () { mTimelines = models; //endInsertRows(); - + */ /* initSipAddressesFromChat(); initSipAddressesFromCalls(); @@ -135,25 +146,50 @@ void TimelineListModel::initTimeline () { */ } -TimelineModel * TimelineListModel::getTimeline(std::shared_ptr chatRoom, const bool &create){ +std::shared_ptr TimelineListModel::getTimeline(std::shared_ptr chatRoom, const bool &create){ if(chatRoom){ for(auto it = mTimelines.begin() ; it != mTimelines.end() ; ++it){ - if( (*it)->getChatModel()->getChatRoom() == chatRoom){ + if( (*it)->getChatRoomModel()->getChatRoom() == chatRoom){ return *it; } } - if(create) - return new TimelineModel(chatRoom); + if(create){ + std::shared_ptr model = std::make_shared(chatRoom); + chatRoom->addListener(model); + connect(model.get(), SIGNAL(selectedChanged(bool)), this, SLOT(selectedHasChanged(bool))); + return model; + } } return nullptr; } +void TimelineListModel::setSelectedCount(int selectedCount){ + if(mSelectedCount != selectedCount) { + mSelectedCount = selectedCount; + if( mSelectedCount <= 1)// Do not send signals when selection is higher than max : this is a transition state + emit selectedCountChanged(mSelectedCount); + } +} + +void TimelineListModel::selectedHasChanged(bool selected){ + if(selected) { + if(mSelectedCount >= 1){// We have more selection than wanted : count select first and unselect after : the final signal will be send only on limit + setSelectedCount(mSelectedCount+1);// It will not send a change signal + for(auto it = mTimelines.begin() ; it != mTimelines.end() ; ++it) + if(it->get() != sender()) + (*it)->setSelected(false); + }else + setSelectedCount(mSelectedCount+1); + } else + setSelectedCount(mSelectedCount-1); +} + void TimelineListModel::updateTimelines () { CoreManager *coreManager = CoreManager::getInstance(); auto currentAddress = coreManager->getAccountSettingsModel()->getUsedSipAddress(); std::list> allChatRooms = coreManager->getCore()->getChatRooms(); - QList models; + QList > models; for(auto itAllChatRooms = allChatRooms.begin() ; itAllChatRooms != allChatRooms.end() ; ++itAllChatRooms){ if((*itAllChatRooms)->getMe()->getAddress()->weakEqual(currentAddress)){ models << getTimeline(*itAllChatRooms, true); diff --git a/linphone-app/src/components/timeline/TimelineListModel.hpp b/linphone-app/src/components/timeline/TimelineListModel.hpp index 91c3cb25c..798838f67 100644 --- a/linphone-app/src/components/timeline/TimelineListModel.hpp +++ b/linphone-app/src/components/timeline/TimelineListModel.hpp @@ -22,7 +22,7 @@ #define TIMELINE_LIST_MODEL_H_ #include -#include "components/chat/ChatModel.hpp" +#include "components/chat-room/ChatRoomModel.hpp" class TimelineModel; // ============================================================================= @@ -30,13 +30,16 @@ class TimelineModel; class TimelineListModel : public QAbstractListModel { Q_OBJECT public: + + Q_PROPERTY(int selectedCount MEMBER mSelectedCount WRITE setSelectedCount NOTIFY selectedCountChanged) TimelineListModel (QObject *parent = Q_NULLPTR); void reset(); void update(); + void selectAll(const bool& selected); TimelineModel * getAt(const int& index); - TimelineModel * getTimeline(std::shared_ptr chatRoom, const bool &create); + std::shared_ptr getTimeline(std::shared_ptr chatRoom, const bool &create); int rowCount (const QModelIndex &index = QModelIndex()) const override; @@ -45,6 +48,14 @@ public: // Remove a chatroom Q_INVOKABLE void remove (TimelineModel *importer); + int mSelectedCount; + + void setSelectedCount(int selectedCount); +public slots: + void selectedHasChanged(bool selected); + +signals: + void selectedCountChanged(int selectedCount); private: bool removeRow (int row, const QModelIndex &parent = QModelIndex()); @@ -55,7 +66,7 @@ private: void initTimeline (); void updateTimelines(); - QList mTimelines; + QList> mTimelines; }; #endif // TIMELINE_LIST_MODEL_H_ diff --git a/linphone-app/src/components/timeline/TimelineModel.cpp b/linphone-app/src/components/timeline/TimelineModel.cpp index a8be81e68..99d00cf68 100644 --- a/linphone-app/src/components/timeline/TimelineModel.cpp +++ b/linphone-app/src/components/timeline/TimelineModel.cpp @@ -21,7 +21,7 @@ #include "components/core/CoreManager.hpp" #include "components/settings/AccountSettingsModel.hpp" #include "components/sip-addresses/SipAddressesModel.hpp" -#include "components/chat/ChatModel.hpp" +#include "components/chat-room/ChatRoomModel.hpp" #include "utils/Utils.hpp" #include "TimelineModel.hpp" @@ -32,29 +32,37 @@ // ============================================================================= TimelineModel::TimelineModel (std::shared_ptr chatRoom, QObject *parent) : QObject(parent) { - mChatModel = CoreManager::getInstance()->getChatModel(chatRoom); + mChatRoomModel = CoreManager::getInstance()->getChatRoomModel(chatRoom); + + QObject::connect(mChatRoomModel.get(), &ChatRoomModel::unreadMessagesCountChanged, this, &TimelineModel::updateUnreadCount); + QObject::connect(mChatRoomModel.get(), &ChatRoomModel::missedCallsCountChanged, this, &TimelineModel::updateUnreadCount); + //mTimestamp = QDateTime::fromMSecsSinceEpoch(mChatRoomModel->getChatRoom()->getLastUpdateTime()); + mSelected = false; +} + +TimelineModel::~TimelineModel(){ } QString TimelineModel::getFullPeerAddress() const{ - return mChatModel->getFullPeerAddress(); + return mChatRoomModel->getFullPeerAddress(); } QString TimelineModel::getFullLocalAddress() const{ - return mChatModel->getLocalAddress(); + return mChatRoomModel->getLocalAddress(); } QString TimelineModel::getUsername() const{ - std::string username = mChatModel->getChatRoom()->getSubject(); + std::string username = mChatRoomModel->getChatRoom()->getSubject(); if(username != ""){ return QString::fromStdString(username); } - username = mChatModel->getChatRoom()->getPeerAddress()->getDisplayName(); + username = mChatRoomModel->getChatRoom()->getPeerAddress()->getDisplayName(); if(username != "") return QString::fromStdString(username); - username = mChatModel->getChatRoom()->getPeerAddress()->getUsername(); + username = mChatRoomModel->getChatRoom()->getPeerAddress()->getUsername(); if(username != "") return QString::fromStdString(username); - return QString::fromStdString(mChatModel->getChatRoom()->getPeerAddress()->asStringUriOnly()); + return QString::fromStdString(mChatRoomModel->getChatRoom()->getPeerAddress()->asStringUriOnly()); } QString TimelineModel::getAvatar() const{ @@ -65,6 +73,52 @@ int TimelineModel::getPresenceStatus() const{ return 0; } -std::shared_ptr TimelineModel::getChatModel() const{ - return mChatModel; +ChatRoomModel *TimelineModel::getChatRoomModel() const{ + return mChatRoomModel.get(); } + +void TimelineModel::setSelected(const bool& selected){ + if(selected != mSelected){ + mSelected = selected; + emit selectedChanged(mSelected); + } +} + +void TimelineModel::updateUnreadCount(){ + if(mSelected){ + mChatRoomModel->resetMessageCount(); + } +} +//---------------------------------------------------------- +//------ CHAT ROOM HANDLERS +//---------------------------------------------------------- + +void TimelineModel::onIsComposingReceived(const std::shared_ptr & chatRoom, const std::shared_ptr & remoteAddress, bool isComposing){ +} +void TimelineModel::onMessageReceived(const std::shared_ptr & chatRoom, const std::shared_ptr & message){} +void TimelineModel::onNewEvent(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){} +void TimelineModel::onChatMessageReceived(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){} +void TimelineModel::onChatMessageSending(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){} +void TimelineModel::onChatMessageSent(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){} +void TimelineModel::onParticipantAdded(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){} +void TimelineModel::onParticipantRemoved(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){} +void TimelineModel::onParticipantAdminStatusChanged(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){} +void TimelineModel::onStateChanged(const std::shared_ptr & chatRoom, linphone::ChatRoom::State newState){} +void TimelineModel::onSecurityEvent(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){} +void TimelineModel::onSubjectChanged(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) +{ + emit usernameChanged(); +} +void TimelineModel::onUndecryptableMessageReceived(const std::shared_ptr & chatRoom, const std::shared_ptr & message){} +void TimelineModel::onParticipantDeviceAdded(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){} +void TimelineModel::onParticipantDeviceRemoved(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){} +void TimelineModel::onConferenceJoined(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){} +void TimelineModel::onConferenceLeft(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){} +void TimelineModel::onEphemeralEvent(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){} +void TimelineModel::onEphemeralMessageTimerStarted(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){} +void TimelineModel::onEphemeralMessageDeleted(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){} +void TimelineModel::onConferenceAddressGeneration(const std::shared_ptr & chatRoom){} +void TimelineModel::onParticipantRegistrationSubscriptionRequested(const std::shared_ptr & chatRoom, const std::shared_ptr & participantAddress){} +void TimelineModel::onParticipantRegistrationUnsubscriptionRequested(const std::shared_ptr & chatRoom, const std::shared_ptr & participantAddress){} +void TimelineModel::onChatMessageShouldBeStored(const std::shared_ptr & chatRoom, const std::shared_ptr & message){} +void TimelineModel::onChatMessageParticipantImdnStateChanged(const std::shared_ptr & chatRoom, const std::shared_ptr & message, const std::shared_ptr & state){} diff --git a/linphone-app/src/components/timeline/TimelineModel.hpp b/linphone-app/src/components/timeline/TimelineModel.hpp index 8b4a00b6f..1e8a38660 100644 --- a/linphone-app/src/components/timeline/TimelineModel.hpp +++ b/linphone-app/src/components/timeline/TimelineModel.hpp @@ -30,23 +30,25 @@ #include "../contact/ContactModel.hpp" -class ChatModel; +class ChatRoomModel; -class TimelineModel : public QObject { +class TimelineModel : public QObject, public linphone::ChatRoomListener { Q_OBJECT public: TimelineModel (std::shared_ptr chatRoom, QObject *parent = Q_NULLPTR); + virtual ~TimelineModel(); Q_PROPERTY(QString fullPeerAddress READ getFullPeerAddress NOTIFY fullPeerAddressChanged) Q_PROPERTY(QString fullLocalAddress READ getFullLocalAddress NOTIFY fullLocalAddressChanged) - Q_PROPERTY(std::shared_ptr chatModel READ getChatModel CONSTANT) + Q_PROPERTY(ChatRoomModel* chatRoomModel READ getChatRoomModel CONSTANT) // Contact - Q_PROPERTY(QString sipAddress READ getFullPeerAddress NOTIFY fullPeerAddressChanged) - Q_PROPERTY(QString username READ getUsername NOTIFY usernameChanged) - Q_PROPERTY(QString avatar READ getAvatar NOTIFY avatarChanged) - Q_PROPERTY(int presenceStatus READ getPresenceStatus NOTIFY presenceStatusChanged) + //Q_PROPERTY(QString sipAddress READ getFullPeerAddress NOTIFY fullPeerAddressChanged) + //Q_PROPERTY(QString username READ getUsername NOTIFY usernameChanged) + //Q_PROPERTY(QString avatar READ getAvatar NOTIFY avatarChanged) + //Q_PROPERTY(int presenceStatus READ getPresenceStatus NOTIFY presenceStatusChanged) + Q_PROPERTY(bool selected MEMBER mSelected WRITE setSelected NOTIFY selectedChanged) QString getFullPeerAddress() const; @@ -56,19 +58,53 @@ public: QString getAvatar() const; int getPresenceStatus() const; + void setSelected(const bool& selected); - Q_INVOKABLE std::shared_ptr getChatModel() const; + + //Q_INVOKABLE std::shared_ptr getChatRoomModel() const; + Q_INVOKABLE ChatRoomModel* getChatRoomModel() const; - QDateTime mTimestamp; - std::shared_ptr mChatModel; + bool mSelected; + //QDateTime mTimestamp; + std::shared_ptr mChatRoomModel; //std::shared_ptr mChatRoom; - + + virtual void onIsComposingReceived(const std::shared_ptr & chatRoom, const std::shared_ptr & remoteAddress, bool isComposing) override; + virtual void onMessageReceived(const std::shared_ptr & chatRoom, const std::shared_ptr & message) override; + virtual void onNewEvent(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; + virtual void onChatMessageReceived(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; + virtual void onChatMessageSending(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; + virtual void onChatMessageSent(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; + virtual void onParticipantAdded(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; + virtual void onParticipantRemoved(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; + virtual void onParticipantAdminStatusChanged(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; + virtual void onStateChanged(const std::shared_ptr & chatRoom, linphone::ChatRoom::State newState) override; + virtual void onSecurityEvent(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; + virtual void onSubjectChanged(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; + virtual void onUndecryptableMessageReceived(const std::shared_ptr & chatRoom, const std::shared_ptr & message) override; + virtual void onParticipantDeviceAdded(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; + virtual void onParticipantDeviceRemoved(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; + virtual void onConferenceJoined(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; + virtual void onConferenceLeft(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; + virtual void onEphemeralEvent(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; + virtual void onEphemeralMessageTimerStarted(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; + virtual void onEphemeralMessageDeleted(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; + virtual void onConferenceAddressGeneration(const std::shared_ptr & chatRoom) override; + virtual void onParticipantRegistrationSubscriptionRequested(const std::shared_ptr & chatRoom, const std::shared_ptr & participantAddress) override; + virtual void onParticipantRegistrationUnsubscriptionRequested(const std::shared_ptr & chatRoom, const std::shared_ptr & participantAddress) override; + virtual void onChatMessageShouldBeStored(const std::shared_ptr & chatRoom, const std::shared_ptr & message) override; + virtual void onChatMessageParticipantImdnStateChanged(const std::shared_ptr & chatRoom, const std::shared_ptr & message, const std::shared_ptr & state) override; + +public slots: + void updateUnreadCount(); + signals: void fullPeerAddressChanged(); void fullLocalAddressChanged(); void usernameChanged(); void avatarChanged(); void presenceStatusChanged(); + void selectedChanged(bool selected); }; diff --git a/linphone-app/src/components/timeline/TimelineProxyModel.cpp b/linphone-app/src/components/timeline/TimelineProxyModel.cpp index ad8f6f7cf..82ab7d3fe 100644 --- a/linphone-app/src/components/timeline/TimelineProxyModel.cpp +++ b/linphone-app/src/components/timeline/TimelineProxyModel.cpp @@ -37,39 +37,50 @@ TimelineProxyModel::TimelineProxyModel (QObject *parent) : QSortFilterProxyModel(parent) { CoreManager *coreManager = CoreManager::getInstance(); AccountSettingsModel *accountSettingsModel = coreManager->getAccountSettingsModel(); - setSourceModel(CoreManager::getInstance()->getTimelineListModel()); + TimelineListModel * model = CoreManager::getInstance()->getTimelineListModel(); + + connect(model, SIGNAL(selectedCountChanged(int)), this, SIGNAL(selectedCountChanged(int))); + + setSourceModel(model); QObject::connect(accountSettingsModel, &AccountSettingsModel::accountSettingsUpdated, this, [this]() { dynamic_cast(sourceModel())->update(); invalidate(); - updateCurrentSelection(); + //updateCurrentSelection(); }); QObject::connect(coreManager->getSipAddressesModel(), &SipAddressesModel::sipAddressReset, this, [this]() { dynamic_cast(sourceModel())->reset(); invalidate();// Invalidate and reload GUI if the model has been reset - updateCurrentSelection(); + //updateCurrentSelection(); }); sort(0); } // ----------------------------------------------------------------------------- -void TimelineProxyModel::setCurrentChatModel(std::shared_ptr data){ - mCurrentChatModel = data; - emit currentChatModelChanged(mCurrentChatModel); - emit currentTimelineChanged(dynamic_cast(sourceModel())->getTimeline(mCurrentChatModel->getChatRoom(), false)); +/* +void TimelineProxyModel::setCurrentChatRoomModel(ChatRoomModel *data){ + mCurrentChatRoomModel = CoreManager::getInstance()->getChatRoomModel(data); + emit currentChatRoomModelChanged(mCurrentChatRoomModel); + if(mCurrentChatRoomModel) + emit currentTimelineChanged(dynamic_cast(sourceModel())->getTimeline(mCurrentChatRoomModel->getChatRoom(), false).get()); + else + emit currentTimelineChanged(nullptr); } -std::shared_ptr TimelineProxyModel::getCurrentChatModel()const{ - return mCurrentChatModel; +ChatRoomModel *TimelineProxyModel::getCurrentChatRoomModel()const{ + return mCurrentChatRoomModel.get(); } void TimelineProxyModel::updateCurrentSelection(){ auto currentAddress = CoreManager::getInstance()->getAccountSettingsModel()->getUsedSipAddress(); - if(mCurrentChatModel && !mCurrentChatModel->getChatRoom()->getMe()->getAddress()->weakEqual(currentAddress) ){ - setCurrentChatModel(nullptr); + if(mCurrentChatRoomModel && !mCurrentChatRoomModel->getChatRoom()->getMe()->getAddress()->weakEqual(currentAddress) ){ + setCurrentChatRoomModel(nullptr); } } - +*/ +void TimelineProxyModel::unselectAll(){ + dynamic_cast(sourceModel())->selectAll(false); +} // ----------------------------------------------------------------------------- bool TimelineProxyModel::filterAcceptsRow (int sourceRow, const QModelIndex &sourceParent) const { @@ -81,5 +92,5 @@ bool TimelineProxyModel::lessThan (const QModelIndex &left, const QModelIndex &r const TimelineModel* a = sourceModel()->data(left).value(); const TimelineModel* b = sourceModel()->data(right).value(); - return a->mTimestamp <= b->mTimestamp; + return a->getChatRoomModel()->mLastUpdateTime >= b->getChatRoomModel()->mLastUpdateTime ; } diff --git a/linphone-app/src/components/timeline/TimelineProxyModel.hpp b/linphone-app/src/components/timeline/TimelineProxyModel.hpp index a2fd7e34d..f6c9247ea 100644 --- a/linphone-app/src/components/timeline/TimelineProxyModel.hpp +++ b/linphone-app/src/components/timeline/TimelineProxyModel.hpp @@ -24,7 +24,7 @@ #include // ============================================================================= -#include "../chat/ChatModel.hpp" +#include "../chat-room/ChatRoomModel.hpp" class TimelineModel; @@ -35,17 +35,19 @@ class TimelineProxyModel : public QSortFilterProxyModel { public: TimelineProxyModel (QObject *parent = Q_NULLPTR); - Q_PROPERTY(std::shared_ptr currentChatModel WRITE setCurrentChatModel READ getCurrentChatModel NOTIFY currentChatModelChanged) + //Q_PROPERTY(ChatRoomModel *currentChatRoomModel WRITE setCurrentChatRoomModel READ getCurrentChatRoomModel NOTIFY currentChatRoomModelChanged) - void updateCurrentSelection(); + //void updateCurrentSelection(); - Q_INVOKABLE void setCurrentChatModel(std::shared_ptr data); - std::shared_ptr getCurrentChatModel() const; + //Q_INVOKABLE void setCurrentChatRoomModel(ChatRoomModel *data); + //ChatRoomModel *getCurrentChatRoomModel() const; + Q_INVOKABLE void unselectAll(); + //Q_INVOKABLE TimelineModel * getTimeline(); signals: - void currentChatModelChanged(std::shared_ptr currentChatModel); - void currentTimelineChanged(TimelineModel * currentTimeline); - + void selectedCountChanged(int selectedCount); +// void currentChatRoomModelChanged(std::shared_ptr currentChatRoomModel); +// void currentTimelineChanged(TimelineModel * currentTimeline); protected: @@ -57,7 +59,7 @@ protected: void handleLocalAddressChanged (const QString &localAddress); - std::shared_ptr mCurrentChatModel; + //std::shared_ptr mCurrentChatRoomModel; }; diff --git a/linphone-app/ui/modules/Linphone/Chat/Chat.js b/linphone-app/ui/modules/Linphone/Chat/Chat.js index b388db54b..e57745b69 100644 --- a/linphone-app/ui/modules/Linphone/Chat/Chat.js +++ b/linphone-app/ui/modules/Linphone/Chat/Chat.js @@ -47,7 +47,7 @@ function getComponentFromEntry (chatEntry) { return 'FileMessage.qml' } - if (chatEntry.type === Linphone.ChatModel.CallEntry) { + if (chatEntry.type === Linphone.ChatRoomModel.CallEntry) { return 'Event.qml' } diff --git a/linphone-app/ui/modules/Linphone/Chat/Event.qml b/linphone-app/ui/modules/Linphone/Chat/Event.qml index 58093cc19..0eb10c89b 100644 --- a/linphone-app/ui/modules/Linphone/Chat/Event.qml +++ b/linphone-app/ui/modules/Linphone/Chat/Event.qml @@ -11,28 +11,28 @@ Row { property string _type: { var status = $chatEntry.status - if (status === ChatModel.CallStatusSuccess) { + if (status === ChatRoomModel.CallStatusSuccess) { if (!$chatEntry.isStart) { return 'ended_call' } return $chatEntry.isOutgoing ? 'outgoing_call' : 'incoming_call' } - if (status === ChatModel.CallStatusDeclined) { + if (status === ChatRoomModel.CallStatusDeclined) { return $chatEntry.isOutgoing ? 'declined_outgoing_call' : 'declined_incoming_call' } - if (status === ChatModel.CallStatusMissed) { + if (status === ChatRoomModel.CallStatusMissed) { return $chatEntry.isOutgoing ? 'missed_outgoing_call' : 'missed_incoming_call' } - if (status === ChatModel.CallStatusAborted) { + if (status === ChatRoomModel.CallStatusAborted) { return $chatEntry.isOutgoing ? 'outgoing_call' : 'incoming_call' } - if (status === ChatModel.CallStatusEarlyAborted) { + if (status === ChatRoomModel.CallStatusEarlyAborted) { return $chatEntry.isOutgoing ? 'missed_outgoing_call' : 'missed_incoming_call' } - if (status === ChatModel.CallStatusAcceptedElsewhere) { + if (status === ChatRoomModel.CallStatusAcceptedElsewhere) { return $chatEntry.isOutgoing ? 'outgoing_call' : 'incoming_call' } - if (status === ChatModel.CallStatusDeclinedElsewhere) { + if (status === ChatRoomModel.CallStatusDeclinedElsewhere) { return $chatEntry.isOutgoing ? 'declined_outgoing_call' : 'declined_incoming_call' } diff --git a/linphone-app/ui/modules/Linphone/Chat/FileMessage.qml b/linphone-app/ui/modules/Linphone/Chat/FileMessage.qml index 77ce9c353..f16f0af6b 100644 --- a/linphone-app/ui/modules/Linphone/Chat/FileMessage.qml +++ b/linphone-app/ui/modules/Linphone/Chat/FileMessage.qml @@ -48,12 +48,12 @@ Row { id: rectangle readonly property bool isError: Utils.includes([ - ChatModel.MessageStatusFileTransferError, - ChatModel.MessageStatusNotDelivered, + ChatRoomModel.MessageStatusFileTransferError, + ChatRoomModel.MessageStatusNotDelivered, ], $chatEntry.status) - readonly property bool isUploaded: $chatEntry.status === ChatModel.MessageStatusDelivered - readonly property bool isDelivered: $chatEntry.status === ChatModel.MessageStatusDeliveredToUser - readonly property bool isRead: $chatEntry.status === ChatModel.MessageStatusDisplayed + readonly property bool isUploaded: $chatEntry.status === ChatRoomModel.MessageStatusDelivered + readonly property bool isDelivered: $chatEntry.status === ChatRoomModel.MessageStatusDeliveredToUser + readonly property bool isRead: $chatEntry.status === ChatRoomModel.MessageStatusDisplayed color: $chatEntry.isOutgoing ? ChatStyle.entry.message.outgoing.backgroundColor @@ -200,7 +200,7 @@ Row { to: $chatEntry.fileSize value: $chatEntry.fileOffset || 0 - visible: $chatEntry.status === ChatModel.MessageStatusInProgress || $chatEntry.status === ChatModel.MessageStatusFileTransferInProgress + visible: $chatEntry.status === ChatRoomModel.MessageStatusInProgress || $chatEntry.status === ChatRoomModel.MessageStatusFileTransferInProgress background: Rectangle { color: ChatStyle.entry.message.file.status.bar.background.color @@ -292,7 +292,7 @@ Row { MouseArea { anchors.fill: parent - visible: (rectangle.isError || $chatEntry.status === ChatModel.MessageStatusIdle) && $chatEntry.isOutgoing + visible: (rectangle.isError || $chatEntry.status === ChatRoomModel.MessageStatusIdle) && $chatEntry.isOutgoing onClicked: proxyModel.resendMessage(index) } } @@ -319,7 +319,7 @@ Row { sourceComponent: $chatEntry.isOutgoing ? ( - $chatEntry.status === ChatModel.MessageStatusInProgress || $chatEntry.status === ChatModel.MessageStatusFileTransferInProgress + $chatEntry.status === ChatRoomModel.MessageStatusInProgress || $chatEntry.status === ChatRoomModel.MessageStatusFileTransferInProgress ? indicator : icon ) : undefined diff --git a/linphone-app/ui/modules/Linphone/Chat/IncomingMessage.qml b/linphone-app/ui/modules/Linphone/Chat/IncomingMessage.qml index 38df2f19e..f1b959301 100644 --- a/linphone-app/ui/modules/Linphone/Chat/IncomingMessage.qml +++ b/linphone-app/ui/modules/Linphone/Chat/IncomingMessage.qml @@ -38,7 +38,7 @@ RowLayout { // 2. Previous entry is a call event. => Visible. // 3. I have sent a message before my contact. => Visible. // 4. One hour between two incoming messages. => Visible. - return previousEntry.type !== ChatModel.MessageEntry || + return previousEntry.type !== ChatRoomModel.MessageEntry || previousEntry.isOutgoing || $chatEntry.timestamp.getTime() - previousEntry.timestamp.getTime() > 3600 } diff --git a/linphone-app/ui/modules/Linphone/Chat/OutgoingMessage.qml b/linphone-app/ui/modules/Linphone/Chat/OutgoingMessage.qml index d6ea734a9..921c49615 100644 --- a/linphone-app/ui/modules/Linphone/Chat/OutgoingMessage.qml +++ b/linphone-app/ui/modules/Linphone/Chat/OutgoingMessage.qml @@ -35,12 +35,12 @@ Item { Icon { id: iconId readonly property var isError: Utils.includes([ - ChatModel.MessageStatusFileTransferError, - ChatModel.MessageStatusNotDelivered, + ChatRoomModel.MessageStatusFileTransferError, + ChatRoomModel.MessageStatusNotDelivered, ], $chatEntry.status) - readonly property bool isUploaded: $chatEntry.status === ChatModel.MessageStatusDelivered - readonly property bool isDelivered: $chatEntry.status === ChatModel.MessageStatusDeliveredToUser - readonly property bool isRead: $chatEntry.status === ChatModel.MessageStatusDisplayed + readonly property bool isUploaded: $chatEntry.status === ChatRoomModel.MessageStatusDelivered + readonly property bool isDelivered: $chatEntry.status === ChatRoomModel.MessageStatusDeliveredToUser + readonly property bool isRead: $chatEntry.status === ChatRoomModel.MessageStatusDisplayed icon: isError ? 'chat_error' @@ -50,7 +50,7 @@ Item { MouseArea { id:retryAction anchors.fill: parent - visible: iconId.isError || $chatEntry.status === ChatModel.MessageStatusIdle + visible: iconId.isError || $chatEntry.status === ChatRoomModel.MessageStatusIdle onClicked: proxyModel.resendMessage(index) } @@ -83,7 +83,7 @@ Item { height: ChatStyle.entry.lineHeight width: ChatStyle.entry.message.outgoing.areaSize - sourceComponent: $chatEntry.status === ChatModel.MessageStatusInProgress || $chatEntry.status === ChatModel.MessageStatusFileTransferInProgress + sourceComponent: $chatEntry.status === ChatRoomModel.MessageStatusInProgress || $chatEntry.status === ChatRoomModel.MessageStatusFileTransferInProgress ? indicator : iconComponent } diff --git a/linphone-app/ui/modules/Linphone/Contact/Contact.qml b/linphone-app/ui/modules/Linphone/Contact/Contact.qml index 03abe44f1..ff8c1d7d0 100644 --- a/linphone-app/ui/modules/Linphone/Contact/Contact.qml +++ b/linphone-app/ui/modules/Linphone/Contact/Contact.qml @@ -43,14 +43,17 @@ Rectangle { Layout.preferredWidth: ContactStyle.contentHeight //image: _contact && _contact.vcard.avatar - image: entry.avatar + image: (entry.contactModel?entry.contactModel.vcard.avatar:entry.avatar?entry.avatar: '') - presenceLevel: entry.presenceStatus != null - ? Presence.getPresenceLevel(entry.presenceStatus) - : -1 + presenceLevel: (entry.contactModel ? Presence.getPresenceLevel(entry.contactModel.presenceStatus) + : entry.presenceStatus ? Presence.getPresenceLevel(entry.presenceStatus) + :-1) //username: LinphoneUtils.getContactUsername(_contact || entry.sipAddress || entry.fullPeerAddress || entry.peerAddress || '') - username: entry.username + username: (entry.contactModel ? entry.contactModel.vcard.username + :entry.username?entry.username: + LinphoneUtils.getContactUsername(entry.sipAddress || entry.fullPeerAddress || entry.peerAddress || '') + ) } ContactDescription { @@ -61,17 +64,18 @@ Rectangle { Layout.leftMargin: ContactStyle.spacing //sipAddress: entry.sipAddress || entry.fullPeerAddress || entry.peerAddress || '' - sipAddress: entry.sipAddress + sipAddress: (entry.contactModel ? entry.contactModel.vcard.sipAddress + :entry.sipAddress || entry.fullPeerAddress || entry.peerAddress || '') username: avatar.username } ContactMessageCounter { Layout.alignment: Qt.AlignTop - count: Number(entry.unreadMessageCount) + Number(entry.missedCallCount) + count: Number(entry.unreadMessagesCount) + Number(entry.missedCallsCount) isComposing: Boolean(entry.isComposing) - visible: item.displayUnreadMessageCount + visible: (entry.unreadMessagesCount !== null || entry.missedCallsCount !== null) && item.displayUnreadMessageCount } } } diff --git a/linphone-app/ui/modules/Linphone/Notifications/NotificationReceivedMessage.qml b/linphone-app/ui/modules/Linphone/Notifications/NotificationReceivedMessage.qml index 553c56dff..906cd654d 100644 --- a/linphone-app/ui/modules/Linphone/Notifications/NotificationReceivedMessage.qml +++ b/linphone-app/ui/modules/Linphone/Notifications/NotificationReceivedMessage.qml @@ -13,7 +13,8 @@ Notification { icon: 'message_sign' // --------------------------------------------------------------------------- - + + readonly property TimelineModel timelineModel: notificationData && notificationData.timelineModel readonly property string peerAddress: notificationData && notificationData.peerAddress || '' readonly property string localAddress: notificationData && notificationData.localAddress || '' readonly property string fullPeerAddress: notificationData && notificationData.fullPeerAddress || '' @@ -22,7 +23,7 @@ Notification { // --------------------------------------------------------------------------- Loader { - active: Boolean(notification.peerAddress) && Boolean(notification.localAddress) + active: timelineModel//Boolean(notification.peerAddress) && Boolean(notification.localAddress) anchors { fill: parent @@ -37,7 +38,8 @@ Notification { Contact { Layout.fillWidth: true - entry: SipAddressesModel.getSipAddressObserver(notification.fullPeerAddress, notification.fullLocalAddress) + //entry: SipAddressesModel.getSipAddressObserver(notification.fullPeerAddress, notification.fullLocalAddress) + entry:notification.timelineModel.getChatRoomModel() } Rectangle { @@ -75,10 +77,11 @@ Notification { onClicked: notification._close(function () { AccountSettingsModel.setDefaultProxyConfigFromSipAddress(notification.localAddress) notification.notificationData.window.setView('Conversation', { + chatRoomModel:notification.timelineModel.getChatRoomModel()/*, peerAddress: notification.peerAddress, localAddress: notification.localAddress, fullPeerAddress: notification.fullPeerAddress, - fullLocalAddress: notification.fullLocalAddress + fullLocalAddress: notification.fullLocalAddress*/ }) }) } diff --git a/linphone-app/ui/modules/Linphone/Timeline/Timeline.qml b/linphone-app/ui/modules/Linphone/Timeline/Timeline.qml index c7da01565..4a6d1e78d 100644 --- a/linphone-app/ui/modules/Linphone/Timeline/Timeline.qml +++ b/linphone-app/ui/modules/Linphone/Timeline/Timeline.qml @@ -44,8 +44,8 @@ Rectangle { Connections { target: model - - onCurrentTimelineChanged:entrySelected(currentTimeline) + onSelectedCountChanged:if(selectedCount<=0) view.currentIndex = -1 + // onCurrentTimelineChanged:entrySelected(currentTimeline) } /* Connections { @@ -113,7 +113,7 @@ Rectangle { width: parent ? parent.width : 0 Contact { - readonly property bool isSelected: view.currentIndex === index + property bool isSelected: modelData.selected //view.currentIndex === index anchors.fill: parent color: isSelected @@ -126,7 +126,7 @@ Rectangle { displayUnreadMessageCount: SettingsModel.chatEnabled //entry: $timelineEntry //entry: SipAddressesModel.getSipAddressObserver(modelData.fullPeerAddress, modelData.fullLocalAddress) - entry: modelData + entry: modelData.chatRoomModel sipAddressColor: isSelected ? TimelineStyle.contact.sipAddress.color.selected : TimelineStyle.contact.sipAddress.color.normal @@ -149,15 +149,14 @@ Rectangle { MouseArea { anchors.fill: parent onClicked: { - view.currentIndex = index - //timeline.model.setCurrentChatModel(modelData.getChatModel())// using member doesn't work - timeline.model.currentChatModel = modelData.chatModel - //timeline.entrySelected(modelData) + //timeline.model.unselectAll() + modelData.selected = true + view.currentIndex = index; + timeline.entrySelected(modelData) //timeline.entrySelected($timelineEntry.sipAddress, $timelineEntry.isSecure) } } } - // onCountChanged: Logic.handleCountChanged(count) } } diff --git a/linphone-app/ui/views/App/Calls/CallsWindow.qml b/linphone-app/ui/views/App/Calls/CallsWindow.qml index 785251b18..87f3ff27d 100644 --- a/linphone-app/ui/views/App/Calls/CallsWindow.qml +++ b/linphone-app/ui/views/App/Calls/CallsWindow.qml @@ -189,10 +189,10 @@ Window { id: chat Chat { - proxyModel: ChatProxyModel { + proxyModel: ChatRoomProxyModel { Component.onCompleted: { if (!SettingsModel.chatEnabled) { - setEntryTypeFilter(ChatModel.CallEntry) + setEntryTypeFilter(ChatRoomModel.CallEntry) } } @@ -205,7 +205,7 @@ Window { Connections { target: SettingsModel - onChatEnabledChanged: proxyModel.setEntryTypeFilter(status ? ChatModel.GenericEntry : ChatModel.CallEntry) + onChatEnabledChanged: proxyModel.setEntryTypeFilter(status ? ChatRoomModel.GenericEntry : ChatRoomModel.CallEntry) } } } diff --git a/linphone-app/ui/views/App/Main/Contacts.qml b/linphone-app/ui/views/App/Main/Contacts.qml index 0b1cdc130..fe4373053 100644 --- a/linphone-app/ui/views/App/Main/Contacts.qml +++ b/linphone-app/ui/views/App/Main/Contacts.qml @@ -151,7 +151,7 @@ ColumnLayout { ActionButton { icon: 'call_chat_unsecure' - onClicked: {console.log("A");actions.itemAt(3).open()} + onClicked: {actions.itemAt(3).open()} } } @@ -181,23 +181,15 @@ ColumnLayout { peerAddress: sipAddress, localAddress: AccountSettingsModel.sipAddress, fullPeerAddress: sipAddress, - fullLocalAddress: AccountSettingsModel.fullSipAddress, - secure:false + fullLocalAddress: AccountSettingsModel.fullSipAddress }) }, function (sipAddress) { - console.log("B") - if(CallsListModel.launchSecureChat(sipAddress)){ - console.log("C") - window.setView('Conversation', { - peerAddress: sipAddress, - localAddress: AccountSettingsModel.sipAddress, - fullPeerAddress: sipAddress, - fullLocalAddress: AccountSettingsModel.fullSipAddress, - secure:true + //Logic.manageAccounts() + window.attachVirtualWindow(Qt.resolvedUrl('Dialogs/ManageChatRoom.qml'), { + //window.setView('Dialogs/ManageChatRoom', { + participantAddress: sipAddress }) - }else - console.log("D") } ] diff --git a/linphone-app/ui/views/App/Main/Conversation.js b/linphone-app/ui/views/App/Main/Conversation.js index df5d1e9d3..fde78b10d 100644 --- a/linphone-app/ui/views/App/Main/Conversation.js +++ b/linphone-app/ui/views/App/Main/Conversation.js @@ -33,7 +33,7 @@ function removeAllEntries () { descriptionText: qsTr('removeAllEntriesDescription'), }, function (status) { if (status) { - chatProxyModel.removeAllEntries() + chatRoomProxyModel.removeAllEntries() } }) } @@ -56,12 +56,12 @@ function getUsername () { } function updateChatFilter (button) { - var ChatModel = Linphone.ChatModel + var ChatRoomModel = Linphone.ChatRoomModel if (button === 0) { - chatProxyModel.setEntryTypeFilter(ChatModel.GenericEntry) + chatRoomProxyModel.setEntryTypeFilter(ChatRoomModel.GenericEntry) } else if (button === 1) { - chatProxyModel.setEntryTypeFilter(ChatModel.CallEntry) + chatRoomProxyModel.setEntryTypeFilter(ChatRoomModel.CallEntry) } else { - chatProxyModel.setEntryTypeFilter(ChatModel.MessageEntry) + chatRoomProxyModel.setEntryTypeFilter(ChatRoomModel.MessageEntry) } } diff --git a/linphone-app/ui/views/App/Main/Conversation.qml b/linphone-app/ui/views/App/Main/Conversation.qml index 6ebd3d79b..8c44b8f83 100644 --- a/linphone-app/ui/views/App/Main/Conversation.qml +++ b/linphone-app/ui/views/App/Main/Conversation.qml @@ -12,13 +12,17 @@ import 'Conversation.js' as Logic ColumnLayout { id: conversation - +/* property string peerAddress property string localAddress property string fullPeerAddress property string fullLocalAddress - property int isSecure - property var chatModel + property int isSecure*/ + property ChatRoomModel chatRoomModel + property string peerAddress : chatRoomModel.getPeerAddress() + property string localAddress : chatRoomModel.getLocalAddress() + property string fullPeerAddress : chatRoomModel.getFullPeerAddress() + property string fullLocalAddress : chatRoomModel.getFullLocalAddress() readonly property var _sipAddressObserver: SipAddressesModel.getSipAddressObserver((fullPeerAddress?fullPeerAddress:peerAddress), (fullLocalAddress?fullLocalAddress:localAddress)) @@ -56,7 +60,8 @@ ColumnLayout { conversation._sipAddressObserver.presenceStatus ) - username: Logic.getUsername() + //username: Logic.getUsername() + username: chatRoomModel.username } ContactDescription { @@ -91,6 +96,14 @@ ColumnLayout { onClicked: CallsListModel.launchAudioCall(conversation.peerAddress) } + ActionButton { + icon: 'call_chat_unsecure' + onClicked: { + window.attachVirtualWindow(Qt.resolvedUrl('Dialogs/ManageChatRoom.qml'), { + //window.setView('Dialogs/ManageChatRoom', { + chatRoomModel:conversation.chatRoomModel + })} + } } ActionBar { @@ -163,17 +176,16 @@ ColumnLayout { Layout.fillHeight: true Layout.fillWidth: true - proxyModel: ChatProxyModel { - id: chatProxyModel + proxyModel: ChatRoomProxyModel { + id: chatRoomProxyModel Component.onCompleted: { if (!SettingsModel.chatEnabled) { - setEntryTypeFilter(ChatModel.CallEntry) + setEntryTypeFilter(ChatRoomModel.CallEntry) } resetMessageCount() } - isSecure: conversation.isSecure - chatModel: conversation.chatModel + chatRoomModel: conversation.chatRoomModel peerAddress: conversation.peerAddress fullPeerAddress: conversation.fullPeerAddress fullLocalAddress: conversation.fullLocalAddress @@ -184,7 +196,7 @@ ColumnLayout { Connections { target: SettingsModel - onChatEnabledChanged: chatProxyModel.setEntryTypeFilter(status ? ChatModel.GenericEntry : ChatModel.CallEntry) + onChatEnabledChanged: chatRoomProxyModel.setEntryTypeFilter(status ? ChatRoomModel.GenericEntry : ChatRoomModel.CallEntry) } Connections { diff --git a/linphone-app/ui/views/App/Main/Dialogs/ManageChatRoom.qml b/linphone-app/ui/views/App/Main/Dialogs/ManageChatRoom.qml new file mode 100644 index 000000000..e3da10eaa --- /dev/null +++ b/linphone-app/ui/views/App/Main/Dialogs/ManageChatRoom.qml @@ -0,0 +1,142 @@ +import QtQuick 2.7 + +import Common 1.0 +import Linphone 1.0 +import Utils 1.0 + +import App.Styles 1.0 + + +// ============================================================================= + +DialogPlus { + property ChatRoomModel chatRoomModel + property var participantAddress : (chatRoomModel?chatRoomModel.getParticipants(): null) + + buttons: [ + TextButtonA { + text: 'cancel' + + onClicked: exit(0) + }, + TextButtonB { + text: 'del' + visible:chatRoomModel + + onClicked: { + if(chatRoomModel){ + chatRoomModel.leaveChatRoom() + exit(0) + } + } + }, + TextButtonB { + text: 'ok' + + onClicked: { + if(chatRoomModel && CallsListModel.createSecureChat(subject.text, participantAddress)) + exit(0) + } + } + ] + + centeredButtons: true + + height: ManageAccountsStyle.height + width: ManageAccountsStyle.width + + // --------------------------------------------------------------------------- + + Form { + anchors.fill: parent + orientation: Qt.Vertical + + FormLine { + + FormGroup { + label: 'Details' + + FormLine { + FormGroup { + label: 'Subject*' + TextField { + id:subject + placeholderText :"Subject" + text:(chatRoomModel?chatRoomModel.getSubject():'') + Keys.onReturnPressed: nextItemInFocusChain().forceActiveFocus() + error : text == '' + TooltipArea{ + text : 'Current subject of the ChatRoom. It cannot be empty' + } + } + } + } + } + } + + + FormLine { + FormGroup { + label: 'Participants : '+participantAddress + /* +ScrollableListViewField { +width: parent.width +height: ManageAccountsStyle.accountSelector.height + +radius: 0 + +ScrollableListView { +id: view + +property string textRole: 'fullSipAddress' // Used by delegate. + +anchors.fill: parent +model: AccountSettingsModel.accounts + +onModelChanged: currentIndex = Utils.findIndex(AccountSettingsModel.accounts, function (account) { +return account.sipAddress === AccountSettingsModel.sipAddress +}) + +delegate: CommonItemDelegate { +id: item +container: view +flattenedModel: modelData +itemIcon: ''//Start with no error and let some time before getting status with the below timer +width: parent.width + +Timer{// This timer is used to synchronize registration state by proxy, without having to deal with change signals +interval: 1000; running: item.visible; repeat: true +onTriggered:itemIcon= Logic.getItemIcon(flattenedModel) +} + +ActionButton { +icon: 'options' +iconSize: 30 +anchors.fill: parent +visible:false +//TODO handle click and jump to proxy config settings +} + +onClicked: { +container.currentIndex = index +if(flattenedModel.proxyConfig) +AccountSettingsModel.setDefaultProxyConfig(flattenedModel.proxyConfig) +else +AccountSettingsModel.setDefaultProxyConfig() +} + +MessageCounter { +anchors.fill: parent +count: flattenedModel.unreadMessageCount+flattenedModel.missedCallCount +} +} +} +} +*/ + } + + } + + } +} + diff --git a/linphone-app/ui/views/App/Main/MainWindow.js b/linphone-app/ui/views/App/Main/MainWindow.js index 3d4053cc6..44cd7dce9 100644 --- a/linphone-app/ui/views/App/Main/MainWindow.js +++ b/linphone-app/ui/views/App/Main/MainWindow.js @@ -114,7 +114,7 @@ function updateSelectedEntry (view, props) { timeline.resetSelectedEntry() } else if (view === 'Contacts') { item.contactsEntry.select() - timeline.resetSelectedEntry() + //timeline.resetSelectedEntry() } else { menu.resetSelectedEntry() /* diff --git a/linphone-app/ui/views/App/Main/MainWindow.qml b/linphone-app/ui/views/App/Main/MainWindow.qml index 5cab562a6..b6033d8bc 100644 --- a/linphone-app/ui/views/App/Main/MainWindow.qml +++ b/linphone-app/ui/views/App/Main/MainWindow.qml @@ -264,13 +264,13 @@ ApplicationWindow { Layout.fillWidth: true model: TimelineProxyModel{} - onEntrySelected: (entry?setView('Conversation', { + onEntrySelected: (entry?setView('Conversation', {/* isSecure:-1, peerAddress: entry.fullPeerAddress, fullPeerAddress: entry.fullPeerAddress, fullLocalAddress: AccountSettingsModel.fullSipAddress, - localAddress: AccountSettingsModel.sipAddress, - chatModel:entry.chatModel + localAddress: AccountSettingsModel.sipAddress,*/ + chatRoomModel:entry.chatRoomModel }): setView('HistoryView', {}) diff --git a/linphone-app/ui/views/App/Settings/Dialogs/SettingsSipAccountsEdit.js b/linphone-app/ui/views/App/Settings/Dialogs/SettingsSipAccountsEdit.js index c38e5d398..26c0462c9 100644 --- a/linphone-app/ui/views/App/Settings/Dialogs/SettingsSipAccountsEdit.js +++ b/linphone-app/ui/views/App/Settings/Dialogs/SettingsSipAccountsEdit.js @@ -50,6 +50,7 @@ function initForm (account) { ) route.text = config.route + conferenceUri.text = config.conferenceUri contactParams.text = config.contactParams avpfInterval.text = config.avpfInterval registerEnabled.checked = config.registerEnabled @@ -70,7 +71,7 @@ function initForm (account) { } function formIsValid () { - return dialog._sipAddressOk && dialog._serverAddressOk && dialog._routeOk + return dialog._sipAddressOk && dialog._serverAddressOk && dialog._routeOk && dialog._conferenceUriOk } // ----------------------------------------------------------------------------- @@ -82,6 +83,7 @@ function validProxyConfig () { registrationDuration: registrationDuration.text, transport: transport.currentText, route: route.text, + conferenceUri: conferenceUri.text, contactParams: contactParams.text, avpfInterval: avpfInterval.text, registerEnabled: registerEnabled.checked, @@ -104,6 +106,9 @@ function validProxyConfig () { function handleRouteChanged (route) { dialog._routeOk = route.length === 0 || Linphone.SipAddressesModel.addressIsValid(route) } +function handleConferenceUriChanged (uri) { + dialog._conferenceUriOk = route.length === 0 || Linphone.SipAddressesModel.addressIsValid(uri) +} function handleServerAddressChanged (address) { if (address.length === 0) { diff --git a/linphone-app/ui/views/App/Settings/Dialogs/SettingsSipAccountsEdit.qml b/linphone-app/ui/views/App/Settings/Dialogs/SettingsSipAccountsEdit.qml index c744962bc..1580baaf7 100644 --- a/linphone-app/ui/views/App/Settings/Dialogs/SettingsSipAccountsEdit.qml +++ b/linphone-app/ui/views/App/Settings/Dialogs/SettingsSipAccountsEdit.qml @@ -16,6 +16,7 @@ DialogPlus { property bool _sipAddressOk: false property bool _serverAddressOk: false property bool _routeOk: false + property bool _conferenceUriOk: false buttons: [ TextButtonA { @@ -123,6 +124,21 @@ DialogPlus { } } } + + FormLine { + FormGroup { + label: 'Conference URI' + + TextField { + id: conferenceUri + + error: dialog._conferenceUriOk ? '' : 'invalid conference uri' + + onTextChanged: Logic.handleConferenceUriChanged(text) + Keys.onReturnPressed: nextItemInFocusChain().forceActiveFocus() + } + } + } FormLine { FormGroup {