Rewrite chat room initializer to do actions with chat room creation delayed.

Hide chat icons if no chat room are available.
This commit is contained in:
Julien Wadel 2022-07-29 15:55:20 +02:00
parent 5d7894e400
commit 1197ded6ec
10 changed files with 202 additions and 155 deletions

View file

@ -174,6 +174,7 @@ set(SOURCES
src/components/chat-events/ChatMessageListener.cpp
src/components/chat-events/ChatMessageModel.cpp
src/components/chat-events/ChatNoticeModel.cpp
src/components/chat-room/ChatRoomInitializer.cpp
src/components/chat-room/ChatRoomListener.cpp
src/components/chat-room/ChatRoomModel.cpp
src/components/chat-room/ChatRoomProxyModel.cpp
@ -265,7 +266,6 @@ set(SOURCES
src/utils/MediastreamerUtils.cpp
src/utils/QExifImageHeader.cpp
src/utils/Utils.cpp
src/utils/hacks/ChatRoomInitializer.cpp
src/utils/plugins/PluginsManager.cpp
)
set(PLUGIN_SOURCES src/utils/plugins/PluginDataAPI.cpp
@ -307,6 +307,7 @@ set(HEADERS
src/components/chat-events/ChatMessageListener.hpp
src/components/chat-events/ChatMessageModel.hpp
src/components/chat-events/ChatNoticeModel.hpp
src/components/chat-room/ChatRoomInitializer.hpp
src/components/chat-room/ChatRoomListener.hpp
src/components/chat-room/ChatRoomModel.hpp
src/components/chat-room/ChatRoomProxyModel.hpp
@ -400,7 +401,6 @@ set(HEADERS
src/utils/MediastreamerUtils.hpp
src/utils/QExifImageHeader.hpp
src/utils/Utils.hpp
src/utils/hacks/ChatRoomInitializer.hpp
src/utils/plugins/PluginsManager.hpp
)
set(PLUGIN_HEADERS

View file

@ -27,6 +27,7 @@
#include "app/App.hpp"
#include "CallListener.hpp"
#include "components/calls/CallsListModel.hpp"
#include "components/chat-room/ChatRoomInitializer.hpp"
#include "components/chat-room/ChatRoomListener.hpp"
#include "components/chat-room/ChatRoomModel.hpp"
#include "components/conference/ConferenceModel.hpp"
@ -55,10 +56,6 @@ void CallModel::connectTo(CallListener * listener){
connect(listener, &CallListener::remoteRecording, this, &CallModel::onRemoteRecording);
}
void CallModel::connectTo(ChatRoomListener * listener){
connect(listener, &ChatRoomListener::stateChanged, this, &CallModel::onChatRoomStateChanged);
}
CallModel::CallModel (shared_ptr<linphone::Call> call){
CoreManager *coreManager = CoreManager::getInstance();
SettingsModel *settings = coreManager->getSettingsModel();
@ -81,8 +78,6 @@ CallModel::CallModel (shared_ptr<linphone::Call> call){
}else
settings->setCameraMode(settings->getCallCameraMode());
}
mDelayedCreationChatRoom.first = std::make_shared<ChatRoomListener>();
connectTo(mDelayedCreationChatRoom.first.get());
// Deal with auto-answer.
if (!isOutgoing()) {
@ -175,9 +170,9 @@ ChatRoomModel * CallModel::getChatRoomModel(){
auto currentParams = mCall->getCurrentParams();
bool isEncrypted = currentParams->getMediaEncryption() != linphone::MediaEncryption::None;
SettingsModel * settingsModel = CoreManager::getInstance()->getSettingsModel();
if(mDelayedCreationChatRoom.second){// We already created a chat room.
if( mDelayedCreationChatRoom.second->getState() == linphone::ChatRoom::State::Created)
return CoreManager::getInstance()->getTimelineListModel()->getChatRoomModel(mDelayedCreationChatRoom.second, true).get();
if(mChatRoom){// We already created a chat room.
if( mChatRoom->getState() == linphone::ChatRoom::State::Created)
return CoreManager::getInstance()->getTimelineListModel()->getChatRoomModel(mChatRoom, true).get();
else// Chat room is not yet created.
return nullptr;
}
@ -209,8 +204,10 @@ ChatRoomModel * CallModel::getChatRoomModel(){
if(chatRoom)
return CoreManager::getInstance()->getTimelineListModel()->getChatRoomModel(chatRoom, true).get();
else{// Wait for creation. Secure chat rooms cannot be used before being created.
mDelayedCreationChatRoom.second = CoreManager::getInstance()->getCore()->createChatRoom(params, callLocalAddress, participants);// The result cannot be used.
mDelayedCreationChatRoom.second->addListener(mDelayedCreationChatRoom.first);
mChatRoom = CoreManager::getInstance()->getCore()->createChatRoom(params, callLocalAddress, participants);
auto initializer = ChatRoomInitializer::create(mChatRoom);
connect(initializer.get(), &ChatRoomInitializer::finished, this, &CallModel::onChatRoomInitialized, Qt::DirectConnection);
ChatRoomInitializer::start(initializer);
return nullptr;
}
}else
@ -937,9 +934,9 @@ void CallModel::onRemoteRecording(const std::shared_ptr<linphone::Call> & call,
emit remoteRecordingChanged(recording);
}
void CallModel::onChatRoomStateChanged(const std::shared_ptr<linphone::ChatRoom> & chatRoom, linphone::ChatRoom::State newState){
if(newState == linphone::ChatRoom::State::Created)// A chat room has been created for the call. Warn listeners that the model changed.
emit chatRoomModelChanged();
void CallModel::onChatRoomInitialized(int state){
qInfo() << "[CallModel] Chat room initialized with state : " << state;
emit chatRoomModelChanged();
}
void CallModel::setRemoteDisplayName(const std::string& name){

View file

@ -33,7 +33,6 @@ class CallListener;
class ConferenceInfoModel;
class ConferenceModel;
class ContactModel;
class ChatRoomListener;
class ChatRoomModel;
class CallModel : public QObject {
@ -186,7 +185,7 @@ public:
std::shared_ptr<linphone::Call> mCall;
std::shared_ptr<CallListener> mCallListener; // This is passed to linpĥone object and must be in shared_ptr
QPair<std::shared_ptr<ChatRoomListener>, std::shared_ptr<linphone::ChatRoom>> mDelayedCreationChatRoom; // For secure chat rooms, we need to wait till it is created by keeping a ref and by using alistener.
std::shared_ptr<linphone::ChatRoom> mChatRoom; // Used chat room for the call.
std::shared_ptr<linphone::Address> mRemoteAddress;
std::shared_ptr<linphone::MagicSearch> mMagicSearch;
@ -195,7 +194,7 @@ public slots:
void searchReceived(std::list<std::shared_ptr<linphone::SearchResult>> results);
void endCall();
void onRemoteRecording(const std::shared_ptr<linphone::Call> & call, bool recording);
void onChatRoomStateChanged(const std::shared_ptr<linphone::ChatRoom> & chatRoom, linphone::ChatRoom::State newState);
void onChatRoomInitialized(int state);
signals:
void callErrorChanged (const QString &callError);
@ -300,7 +299,6 @@ public:
private:
void connectTo(CallListener * listener);
void connectTo(ChatRoomListener * listener);
bool mIsInConference = false;

View file

@ -24,6 +24,7 @@
#include "app/App.hpp"
#include "components/call/CallModel.hpp"
#include "components/chat-room/ChatRoomInitializer.hpp"
#include "components/conference/ConferenceAddModel.hpp"
#include "components/conference/ConferenceHelperModel.hpp"
#include "components/conference/ConferenceModel.hpp"
@ -38,7 +39,7 @@
#include "CallsListModel.hpp"
#include "utils/hacks/ChatRoomInitializer.hpp"
// =============================================================================
@ -340,12 +341,15 @@ QVariantMap CallsListModel::createChatRoom(const QString& subject, const int& se
params->setSubject(subject != ""?Utils::appStringToCoreString(subject):"Dummy Subject");
if( !chatRoom) {
chatRoom = core->createChatRoom(params, localAddress, chatRoomParticipants);
if(chatRoom != nullptr && admins.size() > 0)
ChatRoomInitializer::setAdminsAsync(params->getSubject(), params->getBackend(), params->groupEnabled(), admins );
if(chatRoom != nullptr && admins.size() > 0){
auto initializer = ChatRoomInitializer::create(chatRoom);
initializer->setAdminsData(admins);
ChatRoomInitializer::start(initializer);
}
timeline = timelineList->getTimeline(chatRoom, false);
}else{
if(admins.size() > 0){
ChatRoomInitializer::setAdminsSync(chatRoom, admins);
ChatRoomInitializer::create(chatRoom)->setAdmins(admins);
}
timeline = timelineList->getTimeline(chatRoom, true);
}

View file

@ -0,0 +1,109 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "ChatRoomInitializer.hpp"
#include <QObject>
#include <QDebug>
#include "ChatRoomListener.hpp"
#include "components/core/CoreManager.hpp"
#include "components/core/CoreHandlers.hpp"
// =============================================================================
void ChatRoomInitializer::connectTo(ChatRoomListener * listener){
connect(listener, &ChatRoomListener::conferenceJoined, this, &ChatRoomInitializer::onConferenceJoined);
connect(listener, &ChatRoomListener::stateChanged, this, &ChatRoomInitializer::onStateChanged);
}
// =============================================================================
ChatRoomInitializer::ChatRoomInitializer(std::shared_ptr<linphone::ChatRoom> chatRoom){
mChatRoomListener = std::make_shared<ChatRoomListener>();
connectTo(mChatRoomListener.get());
if( chatRoom){
mChatRoom = chatRoom;
mChatRoom->addListener(mChatRoomListener);
}
}
ChatRoomInitializer::~ChatRoomInitializer(){
if(mChatRoom)
mChatRoom->removeListener(mChatRoomListener);
}
QSharedPointer<ChatRoomInitializer> ChatRoomInitializer::create(std::shared_ptr<linphone::ChatRoom> chatRoom){
QSharedPointer<ChatRoomInitializer> initializer = QSharedPointer<ChatRoomInitializer>::create(chatRoom);
return initializer;
}
void ChatRoomInitializer::setAdminsData(QList< std::shared_ptr<linphone::Address>> admins){
mAdmins = admins;
mAdminsSet = false;
}
void ChatRoomInitializer::setAdmins(QList< std::shared_ptr<linphone::Address>> admins){
if( admins.size() > 0) {
std::list<std::shared_ptr<linphone::Participant>> participants = mChatRoom->getParticipants();
int count = 0;
for(auto participant : participants){
auto address = participant->getAddress();
auto isAdmin = std::find_if(admins.begin(), admins.end(), [address](std::shared_ptr<linphone::Address> addr){
return addr->weakEqual(address);
});
if( isAdmin != admins.end()){
++count;
mChatRoom->setParticipantAdminStatus(participant, true);
}
}
mAdminsSet = true;
qInfo() << "[ChatRoomInitializer] '" << admins.size() << "' admin(s) specified in addition of Me, " << count << " set.";
checkInitialization();
}
}
void ChatRoomInitializer::start(QSharedPointer<ChatRoomInitializer> initializer){
QObject * context = new QObject();
QObject::connect(initializer.get(), &ChatRoomInitializer::finished, context, [context, initializer](int state){
qInfo() << "[ChatRoomInitializer] initialized";
context->deleteLater();// This will destroy context and initializer
});
}
void ChatRoomInitializer::checkInitialization(){
if( mAdmins.size() > 0 && !mAdminsSet)
return;
emit finished((int)mChatRoom->getState());
}
void ChatRoomInitializer::onConferenceJoined(const std::shared_ptr<linphone::ChatRoom> & chatRoom, const std::shared_ptr<const linphone::EventLog> & eventLog) {
qInfo() << "[ChatRoomInitializer] Conference has been set";
setAdmins(mAdmins);
}
void ChatRoomInitializer::onStateChanged(const std::shared_ptr<linphone::ChatRoom> & chatRoom, linphone::ChatRoom::State newState) {
qInfo() << "[ChatRoomInitializer] State : " << (int)newState;
if( newState >= linphone::ChatRoom::State::Created || newState == linphone::ChatRoom::State::Instantiated) {
checkInitialization();
}
}

View file

@ -0,0 +1,67 @@
/*
* Copyright (c) 2022 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 <http://www.gnu.org/licenses/>.
*/
#ifndef CHAT_ROOM_INITIALIZER_H_
#define CHAT_ROOM_INITIALIZER_H_
#include <linphone++/linphone.hh>
#include "ChatRoomInitializer.hpp"
#include <QList>
#include <QObject>
// =============================================================================
class ChatRoomListener;
class ChatRoomInitializer : public QObject{
Q_OBJECT
public:
ChatRoomInitializer(std::shared_ptr<linphone::ChatRoom> chatRoom);
~ChatRoomInitializer();
// DATA to set
QList< std::shared_ptr<linphone::Address>> mAdmins;
bool mAdminsSet = false;
static QSharedPointer<ChatRoomInitializer> create(std::shared_ptr<linphone::ChatRoom> chatRoom); // Return a shared pointer to pass to start function (if delayed creation is needed)
void setAdminsData(QList< std::shared_ptr<linphone::Address>> admins);// Call it to initialize admins for delayed creation.
void setAdmins(QList< std::shared_ptr<linphone::Address>> admins);// set admins directly in chat room from list
static void start(QSharedPointer<ChatRoomInitializer> initializer);// Keep a ref and remove it when initialization is finished.
// Linphone callbacks
virtual void onConferenceJoined(const std::shared_ptr<linphone::ChatRoom> & chatRoom, const std::shared_ptr<const linphone::EventLog> & eventLog);
virtual void onStateChanged(const std::shared_ptr<linphone::ChatRoom> & chatRoom, linphone::ChatRoom::State newState);
signals:
void finished(int state); // this signal is emit before deletion and give the current linphone::ChatRoom:State of the chat room.
private:
void connectTo(ChatRoomListener * listener);
void checkInitialization();// Will send finished() if all are done
std::shared_ptr<linphone::ChatRoom> mChatRoom;
std::shared_ptr<ChatRoomListener> mChatRoomListener; // This need to be a shared_ptr because of adding it to linphone
};
#endif

View file

@ -1,76 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "ChatRoomInitializer.hpp"
#include <QObject>
#include <QDebug>
#include "components/core/CoreManager.hpp"
#include "components/core/CoreHandlers.hpp"
// =============================================================================
ChatRoomInitializer::ChatRoomInitializer(){}
ChatRoomInitializer::~ChatRoomInitializer(){}
void ChatRoomInitializer::onConferenceJoined(const std::shared_ptr<linphone::ChatRoom> & chatRoom, const std::shared_ptr<const linphone::EventLog> & eventLog) {
qInfo() << "[ChatRoomInitializer] Conference has been set";
if(mAdmins.size() > 0){
setAdminsSync(chatRoom, mAdmins);
}
chatRoom->removeListener(mSelf);
mSelf = nullptr;
}
void ChatRoomInitializer::setAdminsSync(const std::shared_ptr<linphone::ChatRoom> & chatRoom, QList< std::shared_ptr<linphone::Address>> admins){
std::list<std::shared_ptr<linphone::Participant>> chatRoomParticipants = chatRoom->getParticipants();
int count = 0;
for(auto participant : chatRoomParticipants){
auto address = participant->getAddress();
auto isAdmin = std::find_if(admins.begin(), admins.end(), [address](std::shared_ptr<linphone::Address> addr){
return addr->weakEqual(address);
});
if( isAdmin != admins.end()){
++count;
chatRoom->setParticipantAdminStatus(participant, true);
}
}
qInfo() << "[ChatRoomInitializer] '" << admins.size() << "' admin(s) specified in addition of Me, " << count << " set.";
}
void ChatRoomInitializer::setAdminsAsync(const std::string& subject, const linphone::ChatRoomBackend& backend, const bool& groupEnabled, QList< std::shared_ptr<linphone::Address>> admins){
QObject * context = new QObject();
QObject::connect(CoreManager::getInstance()->getHandlers().get(), &CoreHandlers::chatRoomStateChanged,
context,[context, admins, subject, backend, groupEnabled](const std::shared_ptr<linphone::ChatRoom> &chatRoomEvent,linphone::ChatRoom::State state){
auto params = chatRoomEvent->getCurrentParams();
if( subject == chatRoomEvent->getSubject() && backend == params->getBackend() && groupEnabled == params->groupEnabled()){
if( state == linphone::ChatRoom::State::Created){
std::shared_ptr<ChatRoomInitializer> init = std::make_shared<ChatRoomInitializer>();
init->mAdmins = admins;
init->mSelf = init;
chatRoomEvent->addListener(init);
context->deleteLater();
}else if( state > linphone::ChatRoom::State::Created){// The chat room could be completed. Delete the bind.
context->deleteLater();
}
}
});
}

View file

@ -1,54 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef CHAT_ROOM_INITIALIZER_H_
#define CHAT_ROOM_INITIALIZER_H_
// Update Admin. We need this hack in order to set parameters without touching on ChatRoom from createChatRoom.
// If something protect the return ChatRoom (like with shared pointer), the future behaviour will be unstable (2 internal instances, wrong ChatRoom objects from callbacks and crash on deletion)
// Thus, we cannot bind to ChatRoom callbacks at the moment of creation and we need to wait for onChatRoomStateChanged from Core Listener and then, react to linphone::ChatRoom::State::Created from the new ChatRoom.
// As we cannot compare exactly the right ChatRoom, we test on subject and parameters that should be enough to be unique at the moment of the creation.
// This is not a 100% (we may protect with a one-time creation) but this workaround should be enough.
// Used on Core::createChatRoom()
#include <linphone++/linphone.hh>
#include <QList>
// =============================================================================
class ChatRoomInitializer : public linphone::ChatRoomListener{
public:
ChatRoomInitializer();
~ChatRoomInitializer();
QList< std::shared_ptr<linphone::Address>> mAdmins;
std::shared_ptr<ChatRoomInitializer> mSelf;
virtual void onConferenceJoined(const std::shared_ptr<linphone::ChatRoom> & chatRoom, const std::shared_ptr<const linphone::EventLog> & eventLog) override;
// Sync : Set Admins to chat room without binding anything (eg. do not wait for any callback and use ChatRoom directly)
static void setAdminsSync(const std::shared_ptr<linphone::ChatRoom> & chatRoom, QList< std::shared_ptr<linphone::Address>> admins);
// Async : Bind to core for onChatRoomStateChanged event and then wait of linphone::ChatRoom::State::Created from ChatRoom listener.
static void setAdminsAsync(const std::string& subject, const linphone::ChatRoomBackend& backend, const bool& groupEnabled, QList< std::shared_ptr<linphone::Address>> admins);
};
#endif

View file

@ -36,6 +36,7 @@ Window {
readonly property bool chatIsOpened: !rightPaned.isClosed()
readonly property bool callsIsOpened: !mainPaned.isClosed()
readonly property bool haveChat: rightPane.sourceComponent
@ -267,6 +268,7 @@ Window {
id: rightPane
anchors.fill: parent
sourceComponent: window.call && window.call.chatRoomModel ? chat : null
onSourceComponentChanged: if(!sourceComponent) window.closeChat()
}
}
}

View file

@ -478,7 +478,7 @@ Rectangle {
isCustom: true
backgroundRadius: width/2
colorSet: IncallStyle.buttons.chat
visible: (SettingsModel.standardChatEnabled || SettingsModel.secureChatEnabled) && callModel && !callModel.isConference
visible: window.haveChat && (SettingsModel.standardChatEnabled || SettingsModel.secureChatEnabled) && callModel && !callModel.isConference
toggled: window.chatIsOpened
onClicked: {
if (window.chatIsOpened) {