From 3aab79261eaffa3f8480cd74034e526b5d92bece Mon Sep 17 00:00:00 2001 From: Julien Wadel Date: Wed, 26 Jan 2022 15:37:08 +0100 Subject: [PATCH] - Chat room loading optimization. - Fix loading more chat entries when going to beginning. - Fix right padding text in chat messages. - Fix black screen videos. --- linphone-app/src/components/camera/Camera.cpp | 25 ++- .../src/components/camera/CameraPreview.cpp | 1 + .../components/chat-room/ChatRoomModel.cpp | 153 ++++++++++-------- .../components/chat-room/ChatRoomModel.hpp | 7 + .../chat-room/ChatRoomProxyModel.cpp | 23 +-- .../chat-room/ChatRoomProxyModel.hpp | 5 + .../src/components/timeline/TimelineModel.cpp | 5 +- .../Common/Animations/BusyIndicator.qml | 2 +- linphone-app/ui/modules/Common/Image/Icon.qml | 1 + .../Common/View/ScrollableListView.qml | 4 +- linphone-app/ui/modules/Linphone/Chat/Chat.js | 56 +++---- .../ui/modules/Linphone/Chat/Chat.qml | 86 +++++----- .../modules/Linphone/Chat/ChatTextMessage.qml | 6 +- .../Linphone/Dialog/SipAddressDialog.qml | 3 +- .../ui/modules/Linphone/History/History.js | 2 +- .../ui/modules/Linphone/History/History.qml | 2 +- .../NotificationReceivedMessage.qml | 1 + .../Linphone/Styles/Chat/ChatStyle.qml | 2 +- .../ui/modules/Linphone/Timeline/Timeline.qml | 5 +- .../ui/views/App/Calls/CallsWindow.qml | 2 - .../ui/views/App/Main/ContactEdit.qml | 1 + linphone-app/ui/views/App/Main/Contacts.qml | 1 + .../ui/views/App/Main/Conversation.qml | 3 +- linphone-app/ui/views/App/Main/MainWindow.qml | 13 +- 24 files changed, 229 insertions(+), 180 deletions(-) diff --git a/linphone-app/src/components/camera/Camera.cpp b/linphone-app/src/components/camera/Camera.cpp index 47a6a21bb..184e4d85c 100644 --- a/linphone-app/src/components/camera/Camera.cpp +++ b/linphone-app/src/components/camera/Camera.cpp @@ -54,22 +54,41 @@ Camera::Camera (QQuickItem *parent) : QQuickFramebufferObject(parent) { mRefreshTimer->start(); } +class SafeFramebuffer : public QQuickFramebufferObject::Renderer{ +public: + SafeFramebuffer(){} + QOpenGLFramebufferObject *createFramebufferObject (const QSize &size) override{ + return new QOpenGLFramebufferObject(size); + } + void render () override{} + void synchronize (QQuickFramebufferObject *item) override{} +}; + QQuickFramebufferObject::Renderer *Camera::createRenderer () const { QQuickFramebufferObject::Renderer * renderer = NULL; if(mIsPreview){ CoreManager::getInstance()->getCore()->setNativePreviewWindowId(NULL);// Reset renderer=(QQuickFramebufferObject::Renderer *)CoreManager::getInstance()->getCore()->getNativePreviewWindowId(); - return renderer; + CoreManager::getInstance()->getCore()->setNativePreviewWindowId(renderer); }else{ auto call = mCallModel->getCall(); if(call){ call->setNativeVideoWindowId(NULL);// Reset - return (QQuickFramebufferObject::Renderer *) call->getNativeVideoWindowId(); + renderer = (QQuickFramebufferObject::Renderer *) call->getNativeVideoWindowId(); + call->setNativeVideoWindowId(renderer); }else{ CoreManager::getInstance()->getCore()->setNativeVideoWindowId(NULL); - return (QQuickFramebufferObject::Renderer *) CoreManager::getInstance()->getCore()->getNativeVideoWindowId(); + renderer = (QQuickFramebufferObject::Renderer *) CoreManager::getInstance()->getCore()->getNativeVideoWindowId(); + CoreManager::getInstance()->getCore()->setNativeVideoWindowId(renderer); } } + + if(renderer) + return renderer; + else{ + qWarning() << "Camera stream couldn't start for Rendering"; + return new SafeFramebuffer(); + } } // ----------------------------------------------------------------------------- diff --git a/linphone-app/src/components/camera/CameraPreview.cpp b/linphone-app/src/components/camera/CameraPreview.cpp index 0d0dd98f9..9a5835476 100644 --- a/linphone-app/src/components/camera/CameraPreview.cpp +++ b/linphone-app/src/components/camera/CameraPreview.cpp @@ -88,6 +88,7 @@ QQuickFramebufferObject::Renderer *CameraPreview::createRenderer () const { QQuickFramebufferObject::Renderer * renderer; CoreManager::getInstance()->getCore()->setNativePreviewWindowId(NULL);// Reset renderer=(QQuickFramebufferObject::Renderer *)CoreManager::getInstance()->getCore()->getNativePreviewWindowId(); + CoreManager::getInstance()->getCore()->setNativePreviewWindowId(renderer); if(renderer) return renderer; else{ diff --git a/linphone-app/src/components/chat-room/ChatRoomModel.cpp b/linphone-app/src/components/chat-room/ChatRoomModel.cpp index 83d7f46c8..92534bbb8 100644 --- a/linphone-app/src/components/chat-room/ChatRoomModel.cpp +++ b/linphone-app/src/components/chat-room/ChatRoomModel.cpp @@ -525,6 +525,9 @@ bool ChatRoomModel::getIsRemoteComposing () const { return mComposers.size() > 0; } +bool ChatRoomModel::isEntriesLoading() const{ + return mEntriesLoading; +} std::shared_ptr ChatRoomModel::getChatRoom(){ return mChatRoom; @@ -885,6 +888,7 @@ void ChatRoomModel::updateNewMessageNotice(const int& count){ } void ChatRoomModel::initEntries(){ + qDebug() << "Internal Entries : Init"; // On call : reinitialize all entries. This allow to free up memory QList > entries; QList prepareEntries; @@ -909,7 +913,7 @@ void ChatRoomModel::initEntries(){ } } EntrySorterHelper::getLimitedSelection(&entries, prepareEntries, mLastEntriesStep, this); - + qDebug() << "Internal Entries : Built"; mIsInitialized = true; if(entries.size() >0){ beginResetModel(); @@ -917,78 +921,95 @@ void ChatRoomModel::initEntries(){ updateNewMessageNotice(mChatRoom->getUnreadMessagesCount()); endResetModel(); } + qDebug() << "Internal Entries : End"; +} +void ChatRoomModel::setEntriesLoading(const bool& loading){ + if( mEntriesLoading != loading){ + mEntriesLoading = loading; + emit entriesLoadingChanged(mEntriesLoading); + qApp->processEvents(); + } } int ChatRoomModel::loadMoreEntries(){ - QList > entries; - QList prepareEntries; -// Get current event count for each type - QVector entriesCounts; - entriesCounts.resize(3); - for(auto itEntries = mEntries.begin() ; itEntries != mEntries.end() ; ++itEntries){ - if( (*itEntries)->mType == MessageEntry) - ++entriesCounts[0]; - else if( (*itEntries)->mType == CallEntry){ - if(dynamic_cast((*itEntries).get())->mIsStart) - ++entriesCounts[1]; - } else - ++entriesCounts[2]; - } + setEntriesLoading(true); + int currentRowCount = rowCount(); + int newEntries = 0; + do{ + QList > entries; + QList prepareEntries; + // Get current event count for each type + QVector entriesCounts; + entriesCounts.resize(3); + for(auto itEntries = mEntries.begin() ; itEntries != mEntries.end() ; ++itEntries){ + if( (*itEntries)->mType == MessageEntry) + ++entriesCounts[0]; + else if( (*itEntries)->mType == CallEntry){ + if(dynamic_cast((*itEntries).get())->mIsStart) + ++entriesCounts[1]; + } else + ++entriesCounts[2]; + } + + // Messages + for (auto &message : mChatRoom->getHistoryRange(entriesCounts[0], entriesCounts[0]+mLastEntriesStep)){ + auto itEntries = mEntries.begin(); + bool haveEntry = false; + while(!haveEntry && itEntries != mEntries.end()){ + auto entry = dynamic_cast(itEntries->get()); + haveEntry = (entry && entry->getChatMessage() == message); + ++itEntries; + } + if(!haveEntry) + prepareEntries << EntrySorterHelper(message->getTime() ,MessageEntry, message); + } -// Messages - for (auto &message : mChatRoom->getHistoryRange(entriesCounts[0], entriesCounts[0]+mLastEntriesStep)){ - auto itEntries = mEntries.begin(); - bool haveEntry = false; - while(!haveEntry && itEntries != mEntries.end()){ - auto entry = dynamic_cast(itEntries->get()); - haveEntry = (entry && entry->getChatMessage() == message); - ++itEntries; + // Calls + bool secureChatEnabled = CoreManager::getInstance()->getSettingsModel()->getSecureChatEnabled(); + bool standardChatEnabled = CoreManager::getInstance()->getSettingsModel()->getStandardChatEnabled(); + + if( isOneToOne() && (secureChatEnabled && !standardChatEnabled && isSecure() + || standardChatEnabled && !isSecure()) ) { + auto callHistory = CallsListModel::getCallHistory(getParticipantAddress(), Utils::coreStringToAppString(mChatRoom->getLocalAddress()->asStringUriOnly())); + int count = 0; + auto itCallHistory = callHistory.begin(); + while(count < entriesCounts[1] && itCallHistory != callHistory.end()){ + ++itCallHistory; + ++count; + } + count = 0; + while( count < mLastEntriesStep && itCallHistory != callHistory.end()){ + prepareEntries << EntrySorterHelper((*itCallHistory)->getStartDate(), CallEntry, *itCallHistory); + ++itCallHistory; + } } - if(!haveEntry) - prepareEntries << EntrySorterHelper(message->getTime() ,MessageEntry, message); - } - -// Calls - bool secureChatEnabled = CoreManager::getInstance()->getSettingsModel()->getSecureChatEnabled(); - bool standardChatEnabled = CoreManager::getInstance()->getSettingsModel()->getStandardChatEnabled(); - - if( isOneToOne() && (secureChatEnabled && !standardChatEnabled && isSecure() - || standardChatEnabled && !isSecure()) ) { - auto callHistory = CallsListModel::getCallHistory(getParticipantAddress(), Utils::coreStringToAppString(mChatRoom->getLocalAddress()->asStringUriOnly())); - int count = 0; - auto itCallHistory = callHistory.begin(); - while(count < entriesCounts[1] && itCallHistory != callHistory.end()){ - ++itCallHistory; - ++count; + // Notices + for (auto &eventLog : mChatRoom->getHistoryRangeEvents(entriesCounts[2], entriesCounts[2]+mLastEntriesStep)){ + auto itEntries = mEntries.begin(); + bool haveEntry = false; + while(!haveEntry && itEntries != mEntries.end()){ + auto entry = dynamic_cast(itEntries->get()); + haveEntry = (entry && entry->getEventLog() && entry->getEventLog() == eventLog); + ++itEntries; + } + if(!haveEntry) + prepareEntries << EntrySorterHelper(eventLog->getCreationTime() , NoticeEntry, eventLog); } - count = 0; - while( count < mLastEntriesStep && itCallHistory != callHistory.end()){ - prepareEntries << EntrySorterHelper((*itCallHistory)->getStartDate(), CallEntry, *itCallHistory); - ++itCallHistory; + EntrySorterHelper::getLimitedSelection(&entries, prepareEntries, mLastEntriesStep, this); + if(entries.size() >0){ + beginInsertRows(QModelIndex(), 0, entries.size()-1); + for(auto entry : entries) + mEntries.prepend(entry); + endInsertRows(); + //emit layoutChanged(); + updateLastUpdateTime(); } - } -// Notices - for (auto &eventLog : mChatRoom->getHistoryRangeEvents(entriesCounts[2], entriesCounts[2]+mLastEntriesStep)){ - auto itEntries = mEntries.begin(); - bool haveEntry = false; - while(!haveEntry && itEntries != mEntries.end()){ - auto entry = dynamic_cast(itEntries->get()); - haveEntry = (entry && entry->getEventLog() && entry->getEventLog() == eventLog); - ++itEntries; - } - if(!haveEntry) - prepareEntries << EntrySorterHelper(eventLog->getCreationTime() , NoticeEntry, eventLog); - } - EntrySorterHelper::getLimitedSelection(&entries, prepareEntries, mLastEntriesStep, this); - if(entries.size() >0){ - beginInsertRows(QModelIndex(), 0, entries.size()-1); - for(auto entry : entries) - mEntries.prepend(entry); - endInsertRows(); - emit layoutChanged(); - updateLastUpdateTime(); - } - return entries.size(); + newEntries = entries.size(); + }while( newEntries>0 && currentRowCount == rowCount()); + currentRowCount = rowCount() - currentRowCount + 1; + setEntriesLoading(false); + emit moreEntriesLoaded(currentRowCount); + return currentRowCount; } //------------------------------------------------- diff --git a/linphone-app/src/components/chat-room/ChatRoomModel.hpp b/linphone-app/src/components/chat-room/ChatRoomModel.hpp index 2a56a06f2..b99277650 100644 --- a/linphone-app/src/components/chat-room/ChatRoomModel.hpp +++ b/linphone-app/src/components/chat-room/ChatRoomModel.hpp @@ -155,6 +155,8 @@ public: Q_PROPERTY(ChatMessageModel * reply READ getReply WRITE setReply NOTIFY replyChanged) + Q_PROPERTY(bool entriesLoading READ isEntriesLoading WRITE setEntriesLoading NOTIFY entriesLoadingChanged) + //ChatRoomModel (const QString &peerAddress, const QString &localAddress, const bool& isSecure); @@ -199,6 +201,7 @@ public: bool isCurrentProxy() const; // Return true if this chat room is Me() is the current proxy bool canHandleParticipants() const; bool getIsRemoteComposing () const; + bool isEntriesLoading() const; ParticipantListModel* getParticipants() const; std::shared_ptr getChatRoom(); QList getComposers(); @@ -208,6 +211,7 @@ public: void setSubject(QString& subject); void setLastUpdateTime(const QDateTime& lastUpdateDate); void updateLastUpdateTime(); + void setEntriesLoading(const bool& loading); void setUnreadMessagesCount(const int& count); void setMissedCallsCount(const int& count); @@ -243,6 +247,7 @@ public: bool mDeleteChatRoom = false; // Use as workaround because of core->deleteChatRoom() that call destructor without takking account of count ref : call it in ChatRoomModel destructor int mLastEntriesStep = 50; // Retrieve a part of the history to avoid too much processing bool mMarkAsReadEnabled = true; + bool mEntriesLoading = false; void insertCall (const std::shared_ptr &callLog); @@ -289,6 +294,8 @@ public slots: signals: bool isRemoteComposingChanged (); + void entriesLoadingChanged(const bool& loading); + void moreEntriesLoaded(const int& count); void allEntriesRemoved (std::shared_ptr model); void lastEntryRemoved (); diff --git a/linphone-app/src/components/chat-room/ChatRoomProxyModel.cpp b/linphone-app/src/components/chat-room/ChatRoomProxyModel.cpp index b5012e025..872746b24 100644 --- a/linphone-app/src/components/chat-room/ChatRoomProxyModel.cpp +++ b/linphone-app/src/components/chat-room/ChatRoomProxyModel.cpp @@ -19,6 +19,7 @@ */ #include +#include #include "app/App.hpp" #include "components/core/CoreManager.hpp" @@ -140,16 +141,16 @@ void ChatRoomProxyModel::compose (const QString& text) { // ----------------------------------------------------------------------------- -void ChatRoomProxyModel::loadMoreEntries () { +void ChatRoomProxyModel::loadMoreEntriesAsync(){ + QTimer::singleShot(10, this, &ChatRoomProxyModel::loadMoreEntries); +} + +void ChatRoomProxyModel::onMoreEntriesLoaded(const int& count){ + emit moreEntriesLoaded(count); +} +void ChatRoomProxyModel::loadMoreEntries() { if(mChatRoomModel ) { - int currentRowCount = rowCount(); - int newEntries = 0; - do{ - newEntries = mChatRoomModel->loadMoreEntries(); - invalidate(); - }while( newEntries>0 && currentRowCount == rowCount()); - currentRowCount = rowCount() - currentRowCount + 1; - emit moreEntriesLoaded(currentRowCount); + mChatRoomModel->loadMoreEntries(); } } @@ -228,7 +229,6 @@ QString ChatRoomProxyModel::getFullPeerAddress () const { void ChatRoomProxyModel::setFullPeerAddress (const QString &peerAddress) { mFullPeerAddress = peerAddress; emit fullPeerAddressChanged(mFullPeerAddress); - //reload(); } QString ChatRoomProxyModel::getFullLocalAddress () const { @@ -238,7 +238,6 @@ QString ChatRoomProxyModel::getFullLocalAddress () const { void ChatRoomProxyModel::setFullLocalAddress (const QString &localAddress) { mFullLocalAddress = localAddress; emit fullLocalAddressChanged(mFullLocalAddress); - //reload(); } bool ChatRoomProxyModel::markAsReadEnabled() const{ @@ -277,6 +276,7 @@ void ChatRoomProxyModel::reload (ChatRoomModel *chatRoomModel) { QObject::disconnect(ChatRoomModel, &ChatRoomModel::messageReceived, this, &ChatRoomProxyModel::handleMessageReceived); QObject::disconnect(ChatRoomModel, &ChatRoomModel::messageSent, this, &ChatRoomProxyModel::handleMessageSent); QObject::disconnect(ChatRoomModel, &ChatRoomModel::markAsReadEnabledChanged, this, &ChatRoomProxyModel::markAsReadEnabledChanged); + QObject::disconnect(ChatRoomModel, &ChatRoomModel::moreEntriesLoaded, this, &ChatRoomProxyModel::onMoreEntriesLoaded); } @@ -289,6 +289,7 @@ void ChatRoomProxyModel::reload (ChatRoomModel *chatRoomModel) { QObject::connect(ChatRoomModel, &ChatRoomModel::messageReceived, this, &ChatRoomProxyModel::handleMessageReceived); QObject::connect(ChatRoomModel, &ChatRoomModel::messageSent, this, &ChatRoomProxyModel::handleMessageSent); QObject::connect(ChatRoomModel, &ChatRoomModel::markAsReadEnabledChanged, this, &ChatRoomProxyModel::markAsReadEnabledChanged); + QObject::connect(ChatRoomModel, &ChatRoomModel::moreEntriesLoaded, this, &ChatRoomProxyModel::onMoreEntriesLoaded); } static_cast(sourceModel())->setSourceModel(mChatRoomModel.get()); diff --git a/linphone-app/src/components/chat-room/ChatRoomProxyModel.hpp b/linphone-app/src/components/chat-room/ChatRoomProxyModel.hpp index 7f8ba3020..e7d2619ec 100644 --- a/linphone-app/src/components/chat-room/ChatRoomProxyModel.hpp +++ b/linphone-app/src/components/chat-room/ChatRoomProxyModel.hpp @@ -55,6 +55,8 @@ public: Q_INVOKABLE QString getDisplayNameComposers()const; Q_INVOKABLE QVariant getAt(int row); + + Q_INVOKABLE void loadMoreEntriesAsync (); Q_INVOKABLE void loadMoreEntries (); Q_INVOKABLE void setEntryTypeFilter (int type); @@ -73,6 +75,9 @@ public: Q_INVOKABLE void setFilterText(const QString& text); +public slots: + void onMoreEntriesLoaded(const int& count); + signals: void peerAddressChanged (const QString &peerAddress); void localAddressChanged (const QString &localAddress); diff --git a/linphone-app/src/components/timeline/TimelineModel.cpp b/linphone-app/src/components/timeline/TimelineModel.cpp index b710c057c..e8fd8a261 100644 --- a/linphone-app/src/components/timeline/TimelineModel.cpp +++ b/linphone-app/src/components/timeline/TimelineModel.cpp @@ -102,10 +102,7 @@ void TimelineModel::setSelected(const bool& selected){ mChatRoomModel->initEntries(); } emit selectedChanged(mSelected); - }else if(selected)// Warning, Setting to true is only when we want to force a selection. It's why we send a signal only in this case. We want avoid to change the counter on timelines that are already unselected. - // This only work because we want at least only one timeline selected - emit selectedChanged(mSelected); - + } } void TimelineModel::updateUnreadCount(){ diff --git a/linphone-app/ui/modules/Common/Animations/BusyIndicator.qml b/linphone-app/ui/modules/Common/Animations/BusyIndicator.qml index f104f8735..3772b5e98 100644 --- a/linphone-app/ui/modules/Common/Animations/BusyIndicator.qml +++ b/linphone-app/ui/modules/Common/Animations/BusyIndicator.qml @@ -39,7 +39,7 @@ BusyIndicator { RotationAnimator { duration: BusyIndicatorStyle.duration loops: Animation.Infinite - running: busyIndicator.visible && busyIndicator.running + running: true target: item from: 0 diff --git a/linphone-app/ui/modules/Common/Image/Icon.qml b/linphone-app/ui/modules/Common/Image/Icon.qml index 71d947bfb..2cce3b32d 100644 --- a/linphone-app/ui/modules/Common/Image/Icon.qml +++ b/linphone-app/ui/modules/Common/Image/Icon.qml @@ -35,6 +35,7 @@ Item { property bool colorOverwriteEnabled : false mipmap: SettingsModel.mipmapEnabled cache: Images.areReadOnlyImages + asynchronous: true //anchors.centerIn: parent anchors.fill: parent diff --git a/linphone-app/ui/modules/Common/View/ScrollableListView.qml b/linphone-app/ui/modules/Common/View/ScrollableListView.qml index 9eea3fe47..33faf5f0a 100644 --- a/linphone-app/ui/modules/Common/View/ScrollableListView.qml +++ b/linphone-app/ui/modules/Common/View/ScrollableListView.qml @@ -41,8 +41,8 @@ ListView { policy: (view.contentHeight > view.height ? ScrollBar.AlwaysOn : ScrollBar.AlwaysOff) } // --------------------------------------------------------------------------- - - boundsBehavior: Flickable.StopAtBounds + boundsMovement: Flickable.StopAtBounds + boundsBehavior: Flickable.DragOverBounds clip: true contentWidth: width - (vScrollBar.visible?vScrollBar.width:0) spacing: 0 diff --git a/linphone-app/ui/modules/Linphone/Chat/Chat.js b/linphone-app/ui/modules/Linphone/Chat/Chat.js index 3a23403b4..b5070780e 100644 --- a/linphone-app/ui/modules/Linphone/Chat/Chat.js +++ b/linphone-app/ui/modules/Linphone/Chat/Chat.js @@ -31,53 +31,53 @@ // ============================================================================= function initView () { - chat.tryToLoadMoreEntries = false - chat.bindToEnd = true + chat.bindToEnd = true + chat.positionViewAtEnd() + if(chat.atYBeginning && !chat.loadingEntries){//Check if we are at beginning + chat.displaying = true + container.proxyModel.loadMoreEntriesAsync() + } } function getComponentFromEntry (chatEntry) { - - if (chatEntry.type === Linphone.ChatRoomModel.CallEntry) { - return 'Event.qml' - } - - if (chatEntry.type === Linphone.ChatRoomModel.NoticeEntry) { - return 'Notice.qml' - } - - return chatEntry.isOutgoing ? 'OutgoingMessage.qml' : 'IncomingMessage.qml' + + if (chatEntry.type === Linphone.ChatRoomModel.CallEntry) { + return 'Event.qml' + } + + if (chatEntry.type === Linphone.ChatRoomModel.NoticeEntry) { + return 'Notice.qml' + } + + return chatEntry.isOutgoing ? 'OutgoingMessage.qml' : 'IncomingMessage.qml' } function handleFilesDropped (files) { - chat.bindToEnd = true - files.forEach(container.proxyModel.sendFileMessage) + chat.bindToEnd = true + files.forEach(container.proxyModel.sendFileMessage) } function handleMoreEntriesLoaded (n) { - chat.positionViewAtIndex(n - 1, QtQuick.ListView.Beginning) + chat.positionViewAtIndex(n - 1, QtQuick.ListView.Beginning) } function handleMovementEnded () { - if (chat.atYEnd) { - chat.bindToEnd = true - } + if (chat.atYEnd) { + chat.bindToEnd = true + } } function handleMovementStarted () { - chat.bindToEnd = false + chat.bindToEnd = false } function handleTextChanged (text) { - container.proxyModel.compose(text) + container.proxyModel.compose(text) } function sendMessage (text) { - textArea.text = '' - chat.bindToEnd = true - if(container.proxyModel) - container.proxyModel.sendMessage(text) - /* - else{// Create a chat room - CallsListModel.createChat() - }*/ + textArea.text = '' + chat.bindToEnd = true + if(container.proxyModel) + container.proxyModel.sendMessage(text) } diff --git a/linphone-app/ui/modules/Linphone/Chat/Chat.qml b/linphone-app/ui/modules/Linphone/Chat/Chat.qml index 6fc36cb0a..fa686cf7d 100644 --- a/linphone-app/ui/modules/Linphone/Chat/Chat.qml +++ b/linphone-app/ui/modules/Linphone/Chat/Chat.qml @@ -39,61 +39,42 @@ Rectangle { id: chat // ----------------------------------------------------------------------- - property bool bindToEnd: false - property bool tryToLoadMoreEntries: true - //property var sipAddressObserver: SipAddressesModel.getSipAddressObserver(proxyModel.fullPeerAddress, proxyModel.fullLocalAddress) + property bool displaying: false + property int loaderCount: 0 + property int readyItems : 0 + property bool loadingLoader: (readyItems != loaderCount) + property bool loadingEntries: container.proxyModel.chatRoomModel.entriesLoading || displaying + property bool tryToLoadMoreEntries: loadingEntries || loadingLoader + property bool isMoving : false // replace moving read-only property to allow using movement signals. + onLoadingEntriesChanged: { + if( loadingEntries && !displaying) + displaying = true + } + //property var sipAddressObserver: SipAddressesModel.getSipAddressObserver(proxyModel.fullPeerAddress, proxyModel.fullLocalAddress) // ----------------------------------------------------------------------- Layout.fillHeight: true Layout.fillWidth: true highlightFollowsCurrentItem: false - + // Use moving event => this is a user action. + onIsMovingChanged:{ + if(!chat.isMoving && chat.atYBeginning && !chat.loadingEntries){// Moving has stopped. Check if we are at beginning + chat.displaying = true + container.proxyModel.loadMoreEntriesAsync() + } + } section { criteria: ViewSection.FullString delegate: sectionHeading property: '$sectionDate' } - - Timer { - id: loadMoreEntriesDelayer - interval: 1 - repeat: false - running: false - - onTriggered: { - chat.positionViewAtBeginning() - container.proxyModel.loadMoreEntries() - } - } - Timer { - // Delay each search by 100ms - id: endOfLoadMoreEntriesDelayer - interval: 100 - repeat: false - running: false - - onTriggered: { - if(chat.atYBeginning){// We are still at the beginning. Try to continue searching - loadMoreEntriesDelayer.start() - }else// We are not at the begining. New search can be done by moving to the top. - chat.tryToLoadMoreEntries = false - } - } - // ----------------------------------------------------------------------- Component.onCompleted: Logic.initView() - - onContentYChanged: { - if (chat.atYBeginning && !chat.tryToLoadMoreEntries) { - chat.tryToLoadMoreEntries = true// Show busy indicator - loadMoreEntriesDelayer.start()// Let GUI time to the busy indicator to be shown - } - } - onMovementEnded: Logic.handleMovementEnded() - onMovementStarted: Logic.handleMovementStarted() + onMovementStarted: {Logic.handleMovementStarted(); chat.isMoving = true} + onMovementEnded: {Logic.handleMovementEnded(); chat.isMoving = false} // ----------------------------------------------------------------------- @@ -104,12 +85,10 @@ Rectangle { // the position is set at end and it can be possible to load // more entries. onEntryTypeFilterChanged: Logic.initView() + onMoreEntriesLoaded: { - Logic.handleMoreEntriesLoaded(n) - if(n>1)// New entries : delay the end - endOfLoadMoreEntriesDelayer.start() - else// No new entries, we can stop without waiting - chat.tryToLoadMoreEntries = false + Logic.handleMoreEntriesLoaded(n)// move view to n - 1 item + chat.displaying = false } } @@ -258,8 +237,18 @@ Rectangle { // Display content. Loader { id: loader + height: (item !== null && typeof(item)!== 'undefined')? item.height: 0 Layout.fillWidth: true source: Logic.getComponentFromEntry($chatEntry) + property int count: 0 + asynchronous: chat.count - count > 100 + onStatusChanged: if( status == Loader.Ready) ++chat.readyItems + Component.onCompleted: count = ++chat.loaderCount + Component.onDestruction: { + --chat.loaderCount + if( status == Loader.Ready) + --chat.readyItems + } } Connections{ target: loader.item @@ -300,16 +289,17 @@ Rectangle { width: parent.width Text { id: composersItem - property var composers : container.proxyModel.chatRoomModel && container.proxyModel.chatRoomModel.composers + property var composers : container.proxyModel.chatRoomModel ? container.proxyModel.chatRoomModel.composers : undefined + property int count : composers && composers.length ? composers.length : 0 color: ChatStyle.composingText.color font.pointSize: ChatStyle.composingText.pointSize height: visible ? undefined : 0 leftPadding: ChatStyle.composingText.leftPadding - visible: composers && composers.length > 0 && ( (!proxyModel.chatRoomModel.haveEncryption && SettingsModel.standardChatEnabled) + visible: count > 0 && ( (!proxyModel.chatRoomModel.haveEncryption && SettingsModel.standardChatEnabled) || (proxyModel.chatRoomModel.haveEncryption && SettingsModel.secureChatEnabled) ) wrapMode: Text.Wrap //: '%1 is typing...' indicate that someone is composing in chat - text:(!composers || composers.length==0?'': qsTr('chatTyping','',composers.length).arg(container.proxyModel.getDisplayNameComposers())) + text:(count==0?'': qsTr('chatTyping','',count).arg(container.proxyModel.getDisplayNameComposers())) } } diff --git a/linphone-app/ui/modules/Linphone/Chat/ChatTextMessage.qml b/linphone-app/ui/modules/Linphone/Chat/ChatTextMessage.qml index ccaf1c4b6..96b5ce12b 100644 --- a/linphone-app/ui/modules/Linphone/Chat/ChatTextMessage.qml +++ b/linphone-app/ui/modules/Linphone/Chat/ChatTextMessage.qml @@ -24,7 +24,7 @@ TextEdit { property string lastTextSelected : '' property font customFont : SettingsModel.textMessageFont property int fitHeight: visible ? contentHeight + padding : 0 - property int fitWidth: visible ? implicitWidth + padding*2 : 0 + property int fitWidth: visible ? implicitWidth + 2: 0 // add 2 because there is a bug on border that lead to not fit text exactly signal rightClicked() @@ -36,10 +36,12 @@ TextEdit { visible: contentModel && contentModel.isText() clip: true padding: ChatStyle.entry.message.padding + textMargin: 0 readOnly: true selectByMouse: true font.family: customFont.family font.pointSize: Units.dp * customFont.pointSize + text: visible ? Utils.encodeTextToQmlRichFormat(contentModel.text, { imagesHeight: ChatStyle.entry.message.images.height, imagesWidth: ChatStyle.entry.message.images.width @@ -51,7 +53,7 @@ TextEdit { textFormat: Text.RichText // To supports links and imgs. wrapMode: TextEdit.Wrap - onCursorRectangleChanged: if(!readOnly) Logic.ensureVisible(cursorRectangle) + //onCursorRectangleChanged: if(!readOnly) Logic.ensureVisible(cursorRectangle) onLinkActivated: Qt.openUrlExternally(link) onSelectedTextChanged:{ if(selectedText != '') lastTextSelected = selectedText diff --git a/linphone-app/ui/modules/Linphone/Dialog/SipAddressDialog.qml b/linphone-app/ui/modules/Linphone/Dialog/SipAddressDialog.qml index 523468d32..f32760f97 100644 --- a/linphone-app/ui/modules/Linphone/Dialog/SipAddressDialog.qml +++ b/linphone-app/ui/modules/Linphone/Dialog/SipAddressDialog.qml @@ -93,8 +93,7 @@ DialogPlus { updateSelectionModels: false anchors.fill: parent model: TimelineProxyModel{} - onEntrySelected:{ - console.log(entry) + onEntryClicked:{ if( entry ) { mainItem.chatRoomSelectedCallback(entry.chatRoomModel) exit(1) diff --git a/linphone-app/ui/modules/Linphone/History/History.js b/linphone-app/ui/modules/Linphone/History/History.js index 6cef48676..f61c57f79 100644 --- a/linphone-app/ui/modules/Linphone/History/History.js +++ b/linphone-app/ui/modules/Linphone/History/History.js @@ -38,7 +38,7 @@ function loadMoreEntries () { if (history.atYBeginning && !history.tryToLoadMoreEntries) { history.tryToLoadMoreEntries = true history.positionViewAtBeginning() - container.proxyModel.loadMoreEntries() + container.proxyModel.loadMoreEntriesAsync() } } diff --git a/linphone-app/ui/modules/Linphone/History/History.qml b/linphone-app/ui/modules/Linphone/History/History.qml index bdb9b6402..a076f71d5 100644 --- a/linphone-app/ui/modules/Linphone/History/History.qml +++ b/linphone-app/ui/modules/Linphone/History/History.qml @@ -49,7 +49,7 @@ Rectangle { Component.onCompleted: Logic.initView() - onContentYChanged: Logic.loadMoreEntries() + onContentYChanged: Logic.loadMoreEntriesAsync() onMovementEnded: Logic.handleMovementEnded() onMovementStarted: Logic.handleMovementStarted() diff --git a/linphone-app/ui/modules/Linphone/Notifications/NotificationReceivedMessage.qml b/linphone-app/ui/modules/Linphone/Notifications/NotificationReceivedMessage.qml index 5cc9fab19..d0cda59e8 100644 --- a/linphone-app/ui/modules/Linphone/Notifications/NotificationReceivedMessage.qml +++ b/linphone-app/ui/modules/Linphone/Notifications/NotificationReceivedMessage.qml @@ -86,6 +86,7 @@ Notification { onClicked: notification._close(function () { AccountSettingsModel.setDefaultProxyConfigFromSipAddress(notification.localAddress) notification.timelineModel.selected = true + console.log("Load conversation from notification") notification.notificationData.window.setView('Conversation', { chatRoomModel:notification.timelineModel.getChatRoomModel() }) diff --git a/linphone-app/ui/modules/Linphone/Styles/Chat/ChatStyle.qml b/linphone-app/ui/modules/Linphone/Styles/Chat/ChatStyle.qml index 92eae750b..1523a51f5 100644 --- a/linphone-app/ui/modules/Linphone/Styles/Chat/ChatStyle.qml +++ b/linphone-app/ui/modules/Linphone/Styles/Chat/ChatStyle.qml @@ -164,7 +164,7 @@ QtObject { } property QtObject message: QtObject { - property int padding: 8 + property int padding: 10 property int radius: 4 property QtObject extraContent: QtObject { diff --git a/linphone-app/ui/modules/Linphone/Timeline/Timeline.qml b/linphone-app/ui/modules/Linphone/Timeline/Timeline.qml index dab3b44a1..d6c682040 100644 --- a/linphone-app/ui/modules/Linphone/Timeline/Timeline.qml +++ b/linphone-app/ui/modules/Linphone/Timeline/Timeline.qml @@ -28,6 +28,7 @@ Rectangle { //signal entrySelected (string entry) signal entrySelected (TimelineModel entry) + signal entryClicked(TimelineModel entry) signal showHistoryRequest() // --------------------------------------------------------------------------- @@ -329,8 +330,8 @@ Rectangle { preventStealing: false onClicked: { if(mouse.button == Qt.LeftButton){ - if(modelData.selected || !view.updateSelectionModels)// Update selection - timeline.entrySelected(modelData) + //if(modelData.selected || !view.updateSelectionModels)// Update selection + timeline.entryClicked(modelData) if(view){ if(view.updateSelectionModels) modelData.selected = true diff --git a/linphone-app/ui/views/App/Calls/CallsWindow.qml b/linphone-app/ui/views/App/Calls/CallsWindow.qml index 9b804b4c5..7c1e2c315 100644 --- a/linphone-app/ui/views/App/Calls/CallsWindow.qml +++ b/linphone-app/ui/views/App/Calls/CallsWindow.qml @@ -216,8 +216,6 @@ Window { fullPeerAddress: window.call.fullPeerAddress fullLocalAddress: window.call.fullLocalAddress localAddress: window.call.localAddress - - onChatRoomModelChanged: if(chatRoomModel) chatRoomModel.initEntries() } Connections { diff --git a/linphone-app/ui/views/App/Main/ContactEdit.qml b/linphone-app/ui/views/App/Main/ContactEdit.qml index a4640194f..b29b73e41 100644 --- a/linphone-app/ui/views/App/Main/ContactEdit.qml +++ b/linphone-app/ui/views/App/Main/ContactEdit.qml @@ -240,6 +240,7 @@ ColumnLayout { sipAddresses: _contact ? _contact.vcard.sipAddresses : [ contactEdit.sipAddress ] function vewConversation(chatRoomModel){ + console.log("Load conversation from contact edit") window.setView('Conversation', { chatRoomModel:chatRoomModel }, function(){ diff --git a/linphone-app/ui/views/App/Main/Contacts.qml b/linphone-app/ui/views/App/Main/Contacts.qml index f60a15072..1edd4c3a5 100644 --- a/linphone-app/ui/views/App/Main/Contacts.qml +++ b/linphone-app/ui/views/App/Main/Contacts.qml @@ -204,6 +204,7 @@ ColumnLayout { Connections{ target: lastChatRoom onStateChanged: if(state === 1) { + console.log("Load conversation from contacts") window.setView('Conversation', { chatRoomModel: lastChatRoom }) diff --git a/linphone-app/ui/views/App/Main/Conversation.qml b/linphone-app/ui/views/App/Main/Conversation.qml index 0f804277f..e9c95ff0d 100644 --- a/linphone-app/ui/views/App/Main/Conversation.qml +++ b/linphone-app/ui/views/App/Main/Conversation.qml @@ -455,7 +455,7 @@ ColumnLayout { anchors.leftMargin: 50 anchors.verticalCenter: parent.verticalCenter //anchors.horizontalCenter: parent.horizontalCenter - visible: chatArea.tryingToLoadMoreEntries + running: chatArea.tryingToLoadMoreEntries } // ------------------------------------------------------------------------- @@ -527,7 +527,6 @@ ColumnLayout { id:chatArea Layout.fillHeight: true Layout.fillWidth: true - proxyModel: ChatRoomProxyModel { id: chatRoomProxyModel diff --git a/linphone-app/ui/views/App/Main/MainWindow.qml b/linphone-app/ui/views/App/Main/MainWindow.qml index b43c90401..7d1dab38e 100644 --- a/linphone-app/ui/views/App/Main/MainWindow.qml +++ b/linphone-app/ui/views/App/Main/MainWindow.qml @@ -319,10 +319,12 @@ ApplicationWindow { onEntrySelected:{ if( entry ) { - window.setView('Conversation', { - chatRoomModel:entry.chatRoomModel - + if( entry.selected){ + console.log("Load conversation from entry selected on timeline") + window.setView('Conversation', { + chatRoomModel:entry.chatRoomModel }) + } }else{ window.setView('Home', {}) @@ -364,11 +366,14 @@ ApplicationWindow { Connections { target: UrlHandlers - onSip: window.setView('Conversation', { + onSip: { + console.log("Change conversation from url handler") + window.setView('Conversation', { peerAddress: sipAddress, localAddress: AccountSettingsModel.sipAddress, fullPeerAddress: sipAddress, fullLocalAddress: AccountSettingsModel.fullSipAddress }) + } } }