From 5b5268ff6055052c84811c3e8dc9c390913c6be8 Mon Sep 17 00:00:00 2001 From: Julien Wadel Date: Thu, 1 Dec 2022 11:25:52 +0100 Subject: [PATCH] Rework of chat room creations for selections. - Avoid to select a chat room that is creating/terminating. - Add a loading spinner when the state of the chat room is updating. - On creation, wait on chat room state before automatically selecting it. --- .../src/components/calls/CallsListModel.cpp | 4 +--- .../chat-room/ChatRoomInitializer.cpp | 4 ++-- .../chat-room/ChatRoomInitializer.hpp | 3 ++- .../components/chat-room/ChatRoomModel.cpp | 9 +++++-- .../components/chat-room/ChatRoomModel.hpp | 10 ++++++-- .../components/timeline/TimelineListModel.cpp | 5 +++- .../src/components/timeline/TimelineModel.cpp | 24 +++++++++++++++++++ .../src/components/timeline/TimelineModel.hpp | 8 +++++++ linphone-app/src/utils/LinphoneEnums.cpp | 9 +++++++ linphone-app/src/utils/LinphoneEnums.hpp | 22 +++++++++++++++-- .../ui/modules/Linphone/Contact/Contact.qml | 22 +++++++++++++++++ .../Linphone/Timeline/TimelineItem.qml | 5 ++-- .../ui/views/App/Main/ContactEdit.qml | 2 +- 13 files changed, 111 insertions(+), 16 deletions(-) diff --git a/linphone-app/src/components/calls/CallsListModel.cpp b/linphone-app/src/components/calls/CallsListModel.cpp index 924960e97..c858c25fa 100644 --- a/linphone-app/src/components/calls/CallsListModel.cpp +++ b/linphone-app/src/components/calls/CallsListModel.cpp @@ -355,9 +355,7 @@ QVariantMap CallsListModel::createChatRoom(const QString& subject, const int& se CoreManager::getInstance()->getTimelineListModel()->mAutoSelectAfterCreation = false; result["chatRoomModel"] = QVariant::fromValue(timeline->getChatRoomModel()); if(selectAfterCreation) {// The timeline here will not receive the first creation event. Set Selected if needed - QTimer::singleShot(200, [timeline](){// Delay process in order to let GUI time for Timeline building/linking before doing actions - timeline->setSelected(true); - }); + timeline->delaySelected(); } } } diff --git a/linphone-app/src/components/chat-room/ChatRoomInitializer.cpp b/linphone-app/src/components/chat-room/ChatRoomInitializer.cpp index 8139f3fc3..97d5c682e 100644 --- a/linphone-app/src/components/chat-room/ChatRoomInitializer.cpp +++ b/linphone-app/src/components/chat-room/ChatRoomInitializer.cpp @@ -83,7 +83,7 @@ void ChatRoomInitializer::setAdmins(QList< std::shared_ptr> a void ChatRoomInitializer::start(QSharedPointer initializer){ QObject * context = new QObject(); - QObject::connect(initializer.get(), &ChatRoomInitializer::finished, context, [context, initializer](int state){ + QObject::connect(initializer.get(), &ChatRoomInitializer::finished, context, [context, initializer](LinphoneEnums::ChatRoomState state){ qDebug() << "[ChatRoomInitializer] initialized"; context->deleteLater();// This will destroy context and initializer }); @@ -93,7 +93,7 @@ void ChatRoomInitializer::checkInitialization(){ if( mAdmins.size() > 0 && !mAdminsSet) return; - emit finished((int)mChatRoom->getState()); + emit finished(LinphoneEnums::fromLinphone(mChatRoom->getState())); } void ChatRoomInitializer::onConferenceJoined(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) { diff --git a/linphone-app/src/components/chat-room/ChatRoomInitializer.hpp b/linphone-app/src/components/chat-room/ChatRoomInitializer.hpp index 3e2efd470..d7852fece 100644 --- a/linphone-app/src/components/chat-room/ChatRoomInitializer.hpp +++ b/linphone-app/src/components/chat-room/ChatRoomInitializer.hpp @@ -24,6 +24,7 @@ #include #include "ChatRoomInitializer.hpp" +#include "utils/LinphoneEnums.hpp" #include @@ -54,7 +55,7 @@ public: virtual void onStateChanged(const std::shared_ptr & 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. + void finished(LinphoneEnums::ChatRoomState state); // this signal is emit before deletion and give the current linphone::ChatRoom:State of the chat room. private: void connectTo(ChatRoomListener * listener); diff --git a/linphone-app/src/components/chat-room/ChatRoomModel.cpp b/linphone-app/src/components/chat-room/ChatRoomModel.cpp index 56249f197..725ca62d0 100644 --- a/linphone-app/src/components/chat-room/ChatRoomModel.cpp +++ b/linphone-app/src/components/chat-room/ChatRoomModel.cpp @@ -149,6 +149,7 @@ ChatRoomModel::ChatRoomModel (const std::shared_ptr& chatRoo QObject::connect(coreManager->getContactsListModel(), &ContactsListModel::contactUpdated, this, &ChatRoomModel::avatarChanged); connect(this, &ChatRoomModel::fullPeerAddressChanged, this, &ChatRoomModel::usernameChanged); + connect(this, &ChatRoomModel::stateChanged, this, &ChatRoomModel::updatingChanged); if(mChatRoom){ mParticipantListModel = QSharedPointer::create(this); @@ -404,8 +405,8 @@ std::list> ChatRoomModel::getParticipants return participantList; } -int ChatRoomModel::getState() const { - return mChatRoom ? (int)mChatRoom->getState() : 0; +LinphoneEnums::ChatRoomState ChatRoomModel::getState() const { + return mChatRoom ? LinphoneEnums::fromLinphone(mChatRoom->getState()) : LinphoneEnums::ChatRoomStateNone; } bool ChatRoomModel::isReadOnly() const{ @@ -481,6 +482,10 @@ bool ChatRoomModel::isBasic() const{ return mChatRoom && mChatRoom->hasCapability((int)linphone::ChatRoomCapabilities::Basic); } +bool ChatRoomModel::isUpdating() const{ + return getState() == LinphoneEnums::ChatRoomStateCreationPending || getState() == LinphoneEnums::ChatRoomStateTerminationPending; +} + std::shared_ptr ChatRoomModel::getChatRoom(){ return mChatRoom; } diff --git a/linphone-app/src/components/chat-room/ChatRoomModel.hpp b/linphone-app/src/components/chat-room/ChatRoomModel.hpp index 9ec963137..962054cc4 100644 --- a/linphone-app/src/components/chat-room/ChatRoomModel.hpp +++ b/linphone-app/src/components/chat-room/ChatRoomModel.hpp @@ -25,6 +25,8 @@ #include "app/proxyModel/ProxyListModel.hpp" #include +#include "utils/LinphoneEnums.hpp" + // ============================================================================= // Fetch all N messages of a ChatRoom. // ============================================================================= @@ -73,13 +75,14 @@ public: Q_PROPERTY(bool isComposing READ getIsRemoteComposing NOTIFY isRemoteComposingChanged) Q_PROPERTY(QList composers READ getComposers NOTIFY isRemoteComposingChanged) Q_PROPERTY(bool isReadOnly READ isReadOnly NOTIFY isReadOnlyChanged) + Q_PROPERTY(bool updating READ isUpdating NOTIFY updatingChanged) Q_PROPERTY(QString sipAddress READ getFullPeerAddress NOTIFY fullPeerAddressChanged) Q_PROPERTY(QString sipAddressUriOnly READ getPeerAddress 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(int state READ getState NOTIFY stateChanged) + Q_PROPERTY(LinphoneEnums::ChatRoomState state READ getState NOTIFY stateChanged) Q_PROPERTY(long ephemeralLifetime READ getEphemeralLifetime WRITE setEphemeralLifetime NOTIFY ephemeralLifetimeChanged) Q_PROPERTY(bool ephemeralEnabled READ isEphemeralEnabled WRITE setEphemeralEnabled NOTIFY ephemeralEnabledChanged) @@ -116,7 +119,7 @@ public: QString getUsername () const; QString getAvatar () const; int getPresenceStatus() const; - int getState() const; + LinphoneEnums::ChatRoomState getState() const; bool isReadOnly() const; bool isEphemeralEnabled() const; long getEphemeralLifetime() const; @@ -135,6 +138,8 @@ public: bool getIsRemoteComposing () const; bool isEntriesLoading() const; bool isBasic() const; + bool isUpdating() const; + ParticipantListModel* getParticipantListModel() const; std::list> getParticipants(const bool& withMe = true) const; std::shared_ptr getChatRoom(); @@ -273,6 +278,7 @@ signals: void markAsReadEnabledChanged(); void chatRoomDeleted();// Must be connected with DirectConnection mode void replyChanged(); + void updatingChanged(); // Chat Room listener callbacks diff --git a/linphone-app/src/components/timeline/TimelineListModel.cpp b/linphone-app/src/components/timeline/TimelineListModel.cpp index 68a26b0b0..7d5d48f55 100644 --- a/linphone-app/src/components/timeline/TimelineListModel.cpp +++ b/linphone-app/src/components/timeline/TimelineListModel.cpp @@ -328,7 +328,10 @@ void TimelineListModel::select(ChatRoomModel * chatRoomModel){ if(chatRoomModel) { auto timeline = getTimeline(chatRoomModel->getChatRoom(), false); if(timeline){ - timeline->setSelected(true); + if(timeline->isUpdating()) + timeline->delaySelected(); + else + timeline->setSelected(true); } } } diff --git a/linphone-app/src/components/timeline/TimelineModel.cpp b/linphone-app/src/components/timeline/TimelineModel.cpp index 68f020fe9..2cccb3a21 100644 --- a/linphone-app/src/components/timeline/TimelineModel.cpp +++ b/linphone-app/src/components/timeline/TimelineModel.cpp @@ -87,6 +87,8 @@ TimelineModel::TimelineModel (std::shared_ptr chatRoom, cons QObject::connect(this, &TimelineModel::selectedChanged, this, &TimelineModel::updateUnreadCount); QObject::connect(CoreManager::getInstance()->getAccountSettingsModel(), &AccountSettingsModel::defaultAccountChanged, this, &TimelineModel::onDefaultAccountChanged); QObject::connect(mChatRoomModel.get(), &ChatRoomModel::chatRoomDeleted, this, &TimelineModel::onChatRoomDeleted); + QObject::connect(mChatRoomModel.get(), &ChatRoomModel::updatingChanged, this, &TimelineModel::updatingChanged); + QObject::connect(mChatRoomModel.get(), &ChatRoomModel::stateChanged, this, &TimelineModel::onChatRoomStateChanged); } if(chatRoom){ mChatRoomListener = std::make_shared(); @@ -103,6 +105,8 @@ TimelineModel::TimelineModel(const TimelineModel * model){ QObject::connect(this, &TimelineModel::selectedChanged, this, &TimelineModel::updateUnreadCount); QObject::connect(CoreManager::getInstance()->getAccountSettingsModel(), &AccountSettingsModel::defaultAccountChanged, this, &TimelineModel::onDefaultAccountChanged); QObject::connect(mChatRoomModel.get(), &ChatRoomModel::chatRoomDeleted, this, &TimelineModel::onChatRoomDeleted); + QObject::connect(mChatRoomModel.get(), &ChatRoomModel::updatingChanged, this, &TimelineModel::updatingChanged); + QObject::connect(mChatRoomModel.get(), &ChatRoomModel::stateChanged, this, &TimelineModel::onChatRoomStateChanged); } if(mChatRoomModel->getChatRoom()){ mChatRoomListener = model->mChatRoomListener; @@ -141,6 +145,10 @@ int TimelineModel::getPresenceStatus() const{ return 0; } +bool TimelineModel::isUpdating() const{ + return !mChatRoomModel || mChatRoomModel->isUpdating(); +} + ChatRoomModel *TimelineModel::getChatRoomModel() const{ return mChatRoomModel.get(); } @@ -165,6 +173,15 @@ void TimelineModel::setSelected(const bool& selected){ } } +void TimelineModel::delaySelected(){ + if( mChatRoomModel->getState() == LinphoneEnums::ChatRoomStateCreated){ + QTimer::singleShot(200, [&](){// Delay process in order to let GUI time for Timeline building/linking before doing actions + setSelected(true); + }); + }else + mDelaySelection = true; +} + void TimelineModel::updateUnreadCount(){ if(!mSelected){// updateUnreadCount is called when selected has changed;: So if mSelected is false then we are going out of it. mChatRoomModel->resetMessageCount();// The reset will appear when the chat room has "mark as read enabled", that means that we should have read messages when going out. @@ -230,4 +247,11 @@ void TimelineModel::onChatMessageParticipantImdnStateChanged(const std::shared_p void TimelineModel::onChatRoomDeleted(){ emit chatRoomDeleted(); +} + +void TimelineModel::onChatRoomStateChanged(){ + if(mDelaySelection && mChatRoomModel->getState() == LinphoneEnums::ChatRoomStateCreated){ + mDelaySelection = false; + setSelected(true); + } } \ No newline at end of file diff --git a/linphone-app/src/components/timeline/TimelineModel.hpp b/linphone-app/src/components/timeline/TimelineModel.hpp index 1c33560e1..34cc2a044 100644 --- a/linphone-app/src/components/timeline/TimelineModel.hpp +++ b/linphone-app/src/components/timeline/TimelineModel.hpp @@ -52,6 +52,7 @@ public: Q_PROPERTY(ChatRoomModel* chatRoomModel READ getChatRoomModel CONSTANT) Q_PROPERTY(bool selected MEMBER mSelected WRITE setSelected NOTIFY selectedChanged) + Q_PROPERTY(bool updating READ isUpdating NOTIFY updatingChanged) QString getFullPeerAddress() const; @@ -61,7 +62,10 @@ public: QString getAvatar() const; int getPresenceStatus() const; + bool isUpdating() const; + void setSelected(const bool& selected); + void delaySelected(); Q_INVOKABLE ChatRoomModel* getChatRoomModel() const; @@ -102,6 +106,7 @@ public slots: void updateUnreadCount(); void onDefaultAccountChanged(); void onChatRoomDeleted(); + void onChatRoomStateChanged(); signals: void fullPeerAddressChanged(); @@ -112,9 +117,12 @@ signals: void selectedChanged(bool selected); void conferenceLeft(); void chatRoomDeleted(); + void updatingChanged(); private: + bool mDelaySelection = false; + void connectTo(ChatRoomListener * listener); std::shared_ptr mChatRoomListener; diff --git a/linphone-app/src/utils/LinphoneEnums.cpp b/linphone-app/src/utils/LinphoneEnums.cpp index e15cc1395..5b8aa6a7a 100644 --- a/linphone-app/src/utils/LinphoneEnums.cpp +++ b/linphone-app/src/utils/LinphoneEnums.cpp @@ -27,6 +27,7 @@ void LinphoneEnums::registerMetaTypes(){ qRegisterMetaType(); qRegisterMetaType(); + qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); @@ -67,6 +68,14 @@ LinphoneEnums::ChatMessageState LinphoneEnums::fromLinphone(const linphone::Chat return static_cast(data); } +linphone::ChatRoom::State LinphoneEnums::toLinphone(const LinphoneEnums::ChatRoomState& data){ + return static_cast(data); +} + +LinphoneEnums::ChatRoomState LinphoneEnums::fromLinphone(const linphone::ChatRoom::State& data){ + return static_cast(data); +} + linphone::Call::Status LinphoneEnums::toLinphone(const LinphoneEnums::CallStatus& data){ return static_cast(data); } diff --git a/linphone-app/src/utils/LinphoneEnums.hpp b/linphone-app/src/utils/LinphoneEnums.hpp index f3bffccf0..4fed4a75c 100644 --- a/linphone-app/src/utils/LinphoneEnums.hpp +++ b/linphone-app/src/utils/LinphoneEnums.hpp @@ -95,8 +95,25 @@ enum ChatMessageState { }; Q_ENUM_NS(ChatMessageState) -linphone::ChatMessage::State toLinphone(const LinphoneEnums::ChatMessageState& capability); -LinphoneEnums::ChatMessageState fromLinphone(const linphone::ChatMessage::State& capability); +linphone::ChatMessage::State toLinphone(const LinphoneEnums::ChatMessageState& data); +LinphoneEnums::ChatMessageState fromLinphone(const linphone::ChatMessage::State& data); + +enum ChatRoomState { + ChatRoomStateNone = int(linphone::ChatRoom::State::None), + ChatRoomStateInstantiated = int(linphone::ChatRoom::State::Instantiated), + ChatRoomStateCreationPending = int(linphone::ChatRoom::State::CreationPending), + ChatRoomStateCreated = int(linphone::ChatRoom::State::Created), + ChatRoomStateCreationFailed = int(linphone::ChatRoom::State::CreationFailed), + ChatRoomStateTerminationPending = int(linphone::ChatRoom::State::TerminationPending), + ChatRoomStateTerminated = int(linphone::ChatRoom::State::Terminated), + ChatRoomStateTerminationFailed = int(linphone::ChatRoom::State::TerminationFailed), + ChatRoomStateDeleted = int(linphone::ChatRoom::State::Deleted), +}; +Q_ENUM_NS(ChatRoomState) + +linphone::ChatRoom::State toLinphone(const LinphoneEnums::ChatRoomState& data); +LinphoneEnums::ChatRoomState fromLinphone(const linphone::ChatRoom::State& data); + enum CallStatus { CallStatusDeclined = int(linphone::Call::Status::Declined), @@ -202,6 +219,7 @@ void fromString(const QString& transportType, LinphoneEnums::TransportType *tran Q_DECLARE_METATYPE(LinphoneEnums::CallStatus) Q_DECLARE_METATYPE(LinphoneEnums::ChatMessageState) +Q_DECLARE_METATYPE(LinphoneEnums::ChatRoomState) Q_DECLARE_METATYPE(LinphoneEnums::ConferenceLayout) Q_DECLARE_METATYPE(LinphoneEnums::ConferenceInfoState) Q_DECLARE_METATYPE(LinphoneEnums::ConferenceSchedulerState) diff --git a/linphone-app/ui/modules/Linphone/Contact/Contact.qml b/linphone-app/ui/modules/Linphone/Contact/Contact.qml index 630ee0ab0..5dcc99f93 100644 --- a/linphone-app/ui/modules/Linphone/Contact/Contact.qml +++ b/linphone-app/ui/modules/Linphone/Contact/Contact.qml @@ -25,6 +25,7 @@ Rectangle { property bool displayUnreadMessageCount: false property bool showSubtitle : true + property bool showBusyIndicator: false property string subtitle: '' property string subject: (entry && entry.conferenceInfoModel && entry.conferenceInfoModel.subject @@ -89,6 +90,27 @@ Rectangle { anchors.fill: parent onClicked: item.avatarClicked(mouse) } + + Loader{ + id: busyLoader + + anchors.fill: parent + anchors.margins: 5 + + active: item.showBusyIndicator + sourceComponent: Component{ + BusyIndicator{// Joining spinner + id: joiningSpinner + running: false + Timer{// Delay starting spinner (Qt bug) + id: indicatorDelay + interval: 100 + onTriggered: joiningSpinner.running = true + } + Component.onCompleted: indicatorDelay.start() + } + } + } } ContactDescription { diff --git a/linphone-app/ui/modules/Linphone/Timeline/TimelineItem.qml b/linphone-app/ui/modules/Linphone/Timeline/TimelineItem.qml index 3e8f34e42..1f0741eff 100644 --- a/linphone-app/ui/modules/Linphone/Timeline/TimelineItem.qml +++ b/linphone-app/ui/modules/Linphone/Timeline/TimelineItem.qml @@ -44,6 +44,7 @@ Item { NumberAnimation { target: optionsView; property: 'x'; to:optionsView.width; duration: 200;} } ] + enabled: !contactView.showBusyIndicator Contact { @@ -68,7 +69,8 @@ Item { ? TimelineStyle.contact.title.color.selected : TimelineStyle.contact.title.color.normal showSubtitle: mainItem.timelineModel && (mainItem.timelineModel.chatRoomModel && (mainItem.timelineModel.chatRoomModel.isOneToOne || !mainItem.timelineModel.chatRoomModel.isConference)) - TooltipArea { + showBusyIndicator: mainItem.timelineModel && mainItem.timelineModel.updating + TooltipArea { id: contactTooltip text: mainItem.timelineModel && UtilsCpp.toDateTimeString(mainItem.timelineModel.chatRoomModel.lastUpdateTime) isClickable: true @@ -160,5 +162,4 @@ Item { } } } - } \ No newline at end of file diff --git a/linphone-app/ui/views/App/Main/ContactEdit.qml b/linphone-app/ui/views/App/Main/ContactEdit.qml index 1ffe07211..d150abf6b 100644 --- a/linphone-app/ui/views/App/Main/ContactEdit.qml +++ b/linphone-app/ui/views/App/Main/ContactEdit.qml @@ -239,7 +239,7 @@ ColumnLayout { sipAddresses: _contact ? _contact.vcard.sipAddresses : [ contactEdit.sipAddress ] function viewConversation(chatRoomModel){ - if( chatRoomModel){ + if( chatRoomModel && !chatRoomModel.updating){ window.setView('Conversation', { chatRoomModel:chatRoomModel }, function(){