From 8cb6261cb3843269084d0b9a5c367778edc0abf5 Mon Sep 17 00:00:00 2001 From: Julien Wadel Date: Fri, 7 May 2021 21:08:38 +0200 Subject: [PATCH] Rework Data models to fit SDK : eg. ChatModel into ChatRoomModel Use models as it is from QML and avoid to use data translations (using address to get chat room replaced by using directly the chat room) Use SDK chat room handlers on ChatRooms instead of general Core Handlers Use a timeline as a proxy of chat rooms Move events managments like unread messages or missed call to chat rooms Add Chat rooms list and proxy, Participants Add prototype for managing secure chat room Add secure group chats feature Convert some deprecated functions Fix contacts display name and allow to show a subject instead of an address Qt bug workaround : remove annoying warnings on Qt 5.15.1 and later Fix secure group chat creation by adding the conference factory address to linphonerc and to the settings panel --- linphone-app/CMakeLists.txt | 12 +- linphone-app/resources.qrc | 1 + linphone-app/src/app/App.cpp | 8 +- linphone-app/src/app/logger/Logger.cpp | 3 +- linphone-app/src/components/Components.hpp | 2 +- .../src/components/call/CallModel.cpp | 9 + .../src/components/call/CallModel.hpp | 9 + .../src/components/calls/CallsListModel.cpp | 23 ++ .../src/components/calls/CallsListModel.hpp | 1 + .../chat-room/ChatRoomListModel.cpp | 96 +++++ .../chat-room/ChatRoomListModel.hpp | 51 +++ .../ChatRoomModel.cpp} | 365 ++++++++++++------ .../components/chat-room/ChatRoomModel.hpp | 242 ++++++++++++ .../ChatRoomProxyModel.cpp} | 146 +++---- .../ChatRoomProxyModel.hpp} | 28 +- .../src/components/chat/ChatModel.hpp | 161 -------- .../src/components/contact/ContactModel.hpp | 3 +- .../src/components/core/CoreHandlers.cpp | 2 +- .../src/components/core/CoreManager.cpp | 75 ++-- .../src/components/core/CoreManager.hpp | 21 +- .../AbstractEventCountNotifier.cpp | 24 +- .../AbstractEventCountNotifier.hpp | 6 +- .../src/components/notifier/Notifier.cpp | 5 +- .../participant/ParticipantModel.cpp | 54 +++ .../participant/ParticipantModel.hpp | 62 +++ .../participant/ParticipantProxyModel.cpp | 293 ++++++++++++++ .../participant/ParticipantProxyModel.hpp | 64 +++ .../settings/AccountSettingsModel.cpp | 2 + .../sip-addresses/SearchSipAddressesModel.cpp | 2 +- .../sip-addresses/SipAddressesModel.cpp | 42 +- .../sip-addresses/SipAddressesModel.hpp | 10 +- .../components/timeline/TimelineListModel.cpp | 64 ++- .../components/timeline/TimelineListModel.hpp | 17 +- .../src/components/timeline/TimelineModel.cpp | 74 +++- .../src/components/timeline/TimelineModel.hpp | 58 ++- .../timeline/TimelineProxyModel.cpp | 37 +- .../timeline/TimelineProxyModel.hpp | 20 +- linphone-app/ui/modules/Linphone/Chat/Chat.js | 2 +- .../ui/modules/Linphone/Chat/Event.qml | 14 +- .../ui/modules/Linphone/Chat/FileMessage.qml | 16 +- .../modules/Linphone/Chat/IncomingMessage.qml | 2 +- .../modules/Linphone/Chat/OutgoingMessage.qml | 14 +- .../ui/modules/Linphone/Contact/Contact.qml | 20 +- .../NotificationReceivedMessage.qml | 11 +- .../ui/modules/Linphone/Timeline/Timeline.qml | 17 +- .../ui/views/App/Calls/CallsWindow.qml | 6 +- linphone-app/ui/views/App/Main/Contacts.qml | 20 +- .../ui/views/App/Main/Conversation.js | 10 +- .../ui/views/App/Main/Conversation.qml | 32 +- .../views/App/Main/Dialogs/ManageChatRoom.qml | 142 +++++++ linphone-app/ui/views/App/Main/MainWindow.js | 2 +- linphone-app/ui/views/App/Main/MainWindow.qml | 6 +- .../Dialogs/SettingsSipAccountsEdit.js | 7 +- .../Dialogs/SettingsSipAccountsEdit.qml | 16 + 54 files changed, 1840 insertions(+), 589 deletions(-) create mode 100644 linphone-app/src/components/chat-room/ChatRoomListModel.cpp create mode 100644 linphone-app/src/components/chat-room/ChatRoomListModel.hpp rename linphone-app/src/components/{chat/ChatModel.cpp => chat-room/ChatRoomModel.cpp} (65%) create mode 100644 linphone-app/src/components/chat-room/ChatRoomModel.hpp rename linphone-app/src/components/{chat/ChatProxyModel.cpp => chat-room/ChatRoomProxyModel.cpp} (51%) rename linphone-app/src/components/{chat/ChatProxyModel.hpp => chat-room/ChatRoomProxyModel.hpp} (84%) delete mode 100644 linphone-app/src/components/chat/ChatModel.hpp create mode 100644 linphone-app/src/components/participant/ParticipantModel.cpp create mode 100644 linphone-app/src/components/participant/ParticipantModel.hpp create mode 100644 linphone-app/src/components/participant/ParticipantProxyModel.cpp create mode 100644 linphone-app/src/components/participant/ParticipantProxyModel.hpp create mode 100644 linphone-app/ui/views/App/Main/Dialogs/ManageChatRoom.qml 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 {