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.
This commit is contained in:
Julien Wadel 2022-12-01 11:25:52 +01:00
parent 05e7ada86e
commit 5b5268ff60
13 changed files with 111 additions and 16 deletions

View file

@ -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();
}
}
}

View file

@ -83,7 +83,7 @@ void ChatRoomInitializer::setAdmins(QList< std::shared_ptr<linphone::Address>> a
void ChatRoomInitializer::start(QSharedPointer<ChatRoomInitializer> 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<linphone::ChatRoom> & chatRoom, const std::shared_ptr<const linphone::EventLog> & eventLog) {

View file

@ -24,6 +24,7 @@
#include <linphone++/linphone.hh>
#include "ChatRoomInitializer.hpp"
#include "utils/LinphoneEnums.hpp"
#include <QList>
@ -54,7 +55,7 @@ public:
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.
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);

View file

@ -149,6 +149,7 @@ ChatRoomModel::ChatRoomModel (const std::shared_ptr<linphone::ChatRoom>& 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<ParticipantListModel>::create(this);
@ -404,8 +405,8 @@ std::list<std::shared_ptr<linphone::Participant>> 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<linphone::ChatRoom> ChatRoomModel::getChatRoom(){
return mChatRoom;
}

View file

@ -25,6 +25,8 @@
#include "app/proxyModel/ProxyListModel.hpp"
#include <QDateTime>
#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<QString> 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<std::shared_ptr<linphone::Participant>> getParticipants(const bool& withMe = true) const;
std::shared_ptr<linphone::ChatRoom> getChatRoom();
@ -273,6 +278,7 @@ signals:
void markAsReadEnabledChanged();
void chatRoomDeleted();// Must be connected with DirectConnection mode
void replyChanged();
void updatingChanged();
// Chat Room listener callbacks

View file

@ -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);
}
}
}

View file

@ -87,6 +87,8 @@ TimelineModel::TimelineModel (std::shared_ptr<linphone::ChatRoom> 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<ChatRoomListener>();
@ -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);
}
}

View file

@ -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<ChatRoomListener> mChatRoomListener;

View file

@ -27,6 +27,7 @@
void LinphoneEnums::registerMetaTypes(){
qRegisterMetaType<LinphoneEnums::CallStatus>();
qRegisterMetaType<LinphoneEnums::ChatMessageState>();
qRegisterMetaType<LinphoneEnums::ChatRoomState>();
qRegisterMetaType<LinphoneEnums::ConferenceLayout>();
qRegisterMetaType<LinphoneEnums::ConferenceInfoState>();
qRegisterMetaType<LinphoneEnums::ConferenceSchedulerState>();
@ -67,6 +68,14 @@ LinphoneEnums::ChatMessageState LinphoneEnums::fromLinphone(const linphone::Chat
return static_cast<LinphoneEnums::ChatMessageState>(data);
}
linphone::ChatRoom::State LinphoneEnums::toLinphone(const LinphoneEnums::ChatRoomState& data){
return static_cast<linphone::ChatRoom::State>(data);
}
LinphoneEnums::ChatRoomState LinphoneEnums::fromLinphone(const linphone::ChatRoom::State& data){
return static_cast<LinphoneEnums::ChatRoomState>(data);
}
linphone::Call::Status LinphoneEnums::toLinphone(const LinphoneEnums::CallStatus& data){
return static_cast<linphone::Call::Status>(data);
}

View file

@ -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)

View file

@ -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 {

View file

@ -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 {
}
}
}
}

View file

@ -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(){