diff --git a/plugins/sms/conversationsdbusinterface.h b/plugins/sms/conversationsdbusinterface.h --- a/plugins/sms/conversationsdbusinterface.h +++ b/plugins/sms/conversationsdbusinterface.h @@ -61,6 +61,15 @@ */ QVariantList activeConversations(); + /** + * Request the specified range of the specified conversation + * + * Emit conversationUpdated for every requested message + * + * If the conversation does not have enough messages to fill the request, + * this method may return fewer messages + * + */ void requestConversation(const QString &conversationID, int start, int end); /** @@ -77,7 +86,6 @@ Q_SCRIPTABLE void conversationCreated(const QVariantMap& msg); Q_SCRIPTABLE void conversationRemoved(const QString& threadID); Q_SCRIPTABLE void conversationUpdated(const QVariantMap& msg) const; - Q_SCRIPTABLE void conversationMessageReceived(const QVariantMap& msg, int pos) const; private /*methods*/: QString newId(); //Generates successive identifitiers to use as public ids diff --git a/plugins/sms/conversationsdbusinterface.cpp b/plugins/sms/conversationsdbusinterface.cpp --- a/plugins/sms/conversationsdbusinterface.cpp +++ b/plugins/sms/conversationsdbusinterface.cpp @@ -66,23 +66,26 @@ void ConversationsDbusInterface::requestConversation(const QString& conversationID, int start, int end) { - const auto messagesList = m_conversations[conversationID].values(); + auto messagesList = m_conversations[conversationID].values(); if (messagesList.isEmpty()) { // Since there are no messages in the conversation, it's likely that it is a junk ID, but go ahead anyway qCWarning(KDECONNECT_CONVERSATIONS) << "Got a conversationID for a conversation with no messages!" << conversationID; } - // TODO: Check local cache before requesting new messages - // TODO: Make Android interface capable of requesting small window of messages - m_smsInterface.requestConversation(conversationID); + if (!(messagesList.length() > end)) { + // If we don't have enough messages in cache, go get some more + // TODO: Make Android interface capable of requesting small window of messages + m_smsInterface.requestConversation(conversationID); + messagesList = m_conversations[conversationID].values(); + } // Messages are sorted in ascending order of keys, meaning the front of the list has the oldest // messages (smallest timestamp number) // Therefore, return the end of the list first (most recent messages) int i = start; for(auto it = messagesList.crbegin() + start; it != messagesList.crend(); ++it) { - Q_EMIT conversationMessageReceived(it->toVariant(), i); + Q_EMIT conversationUpdated(it->toVariant()); i++; if (i >= end) { break; diff --git a/plugins/sms/smsplugin.h b/plugins/sms/smsplugin.h --- a/plugins/sms/smsplugin.h +++ b/plugins/sms/smsplugin.h @@ -109,6 +109,8 @@ /** * Send a request to the remote for a particular conversation + * + * TODO: Make interface capable of requesting limited window of messages */ Q_SCRIPTABLE void requestConversation(const QString& conversationID) const; @@ -126,7 +128,6 @@ QDBusInterface m_telepathyInterface; ConversationsDbusInterface* m_conversationInterface; - }; #endif diff --git a/plugins/sms/smsplugin.cpp b/plugins/sms/smsplugin.cpp --- a/plugins/sms/smsplugin.cpp +++ b/plugins/sms/smsplugin.cpp @@ -77,10 +77,13 @@ void SmsPlugin::requestConversation (const QString& conversationID) const { + qint32 threadID = conversationID.toInt(); NetworkPacket np(PACKET_TYPE_SMS_REQUEST_CONVERSATION); - np.set("threadID", conversationID.toInt()); + np.set("threadID", threadID); sendPacket(np); + + return; } void SmsPlugin::forwardToTelepathy(const ConversationMessage& message) diff --git a/smsapp/conversationmodel.h b/smsapp/conversationmodel.h --- a/smsapp/conversationmodel.h +++ b/smsapp/conversationmodel.h @@ -54,6 +54,7 @@ void setDeviceId(const QString &/*deviceId*/); Q_INVOKABLE void sendReplyToConversation(const QString& message); + Q_INVOKABLE void requestMoreMessages(const quint32& howMany = 10); private Q_SLOTS: void createRowFromMessage(const QVariantMap &msg, int pos); diff --git a/smsapp/conversationmodel.cpp b/smsapp/conversationmodel.cpp --- a/smsapp/conversationmodel.cpp +++ b/smsapp/conversationmodel.cpp @@ -52,7 +52,7 @@ m_threadId = threadId; clear(); if (!threadId.isEmpty()) { - m_conversationsInterface->requestConversation(threadId, 0, 10); + requestMoreMessages(); } } @@ -63,15 +63,13 @@ qCDebug(KDECONNECT_SMS_CONVERSATION_MODEL) << "setDeviceId" << "of" << this; if (m_conversationsInterface) { - disconnect(m_conversationsInterface, SIGNAL(conversationMessageReceived(QVariantMap, int)), this, SLOT(createRowFromMessage(QVariantMap, int))); disconnect(m_conversationsInterface, SIGNAL(conversationUpdated(QVariantMap)), this, SLOT(handleConversationUpdate(QVariantMap))); delete m_conversationsInterface; } m_deviceId = deviceId; m_conversationsInterface = new DeviceConversationsDbusInterface(deviceId, this); - connect(m_conversationsInterface, SIGNAL(conversationMessageReceived(QVariantMap,int)), this, SLOT(createRowFromMessage(QVariantMap,int))); connect(m_conversationsInterface, SIGNAL(conversationUpdated(QVariantMap)), this, SLOT(handleConversationUpdate(QVariantMap))); } @@ -81,6 +79,15 @@ m_conversationsInterface->replyToConversation(m_threadId, message); } +void ConversationModel::requestMoreMessages(const quint32& howMany) +{ + if (m_threadId.isEmpty()) { + return; + } + const auto& numMessages = rowCount(); + m_conversationsInterface->requestConversation(m_threadId, numMessages, numMessages + howMany); +} + void ConversationModel::createRowFromMessage(const QVariantMap& msg, int pos) { const ConversationMessage message(msg); diff --git a/smsapp/qml/ConversationDisplay.qml b/smsapp/qml/ConversationDisplay.qml --- a/smsapp/qml/ConversationDisplay.qml +++ b/smsapp/qml/ConversationDisplay.qml @@ -38,7 +38,21 @@ property string phoneNumber title: person.person && person.person.name ? person.person.name : phoneNumber + /** + * Build a chat message which is representative of all chat messages + * + * In other words, one which I can use to get a reasonable height guess + */ + ChatMessage { + id: genericMessage + messageBody: "Generic Message Body" + dateTime: new Date('2000-0-0') + visible: false + enabled: false + } + ListView { + id: viewport model: QSortFilterProxyModel { id: model sortOrder: Qt.AscendingOrder @@ -55,12 +69,46 @@ messageBody: model.display sentByMe: model.fromMe dateTime: new Date(model.date) + + ListView.onAdd: { + if (index == viewport.count - 1) + // This message is being inserted at the newest position + // We want to scroll to show it if the user is "almost" looking at it + + // Define some fudge area. If the message is being drawn offscreen but within + // this distance, we move to show it anyway. + // Selected to be genericMessage.height because that value scales for different + // font sizes / DPI / etc. -- Better ideas are welcome! + // Double the value works nicely + var offscreenFudge = 2 * genericMessage.height + + var viewportYBottom = viewport.contentY + viewport.height + + if (y < viewportYBottom + genericMessage.height) { + viewport.currentIndex = index + } + } } - // Set the view to start at the bottom of the page and track new elements if it was not manually scrolled up - currentIndex: atYEnd ? - count - 1 : - currentIndex + onMovementEnded: { + // Unset the highlightRangeMode if it was set previously + highlightRangeMode = ListView.ApplyRange + highlightMoveDuration: -1 // "Re-enable" the highlight animation + + if (atYBeginning) { + // "Lock" the view to the message currently at the beginning of the view + // This prevents the view from snapping to the top of the messages we are about to request + currentIndex = 0 // Index 0 is the beginning of the view + preferredHighlightBegin = visibleArea.yPosition + preferredHighlightEnd = preferredHighlightBegin + currentItem.height + highlightRangeMode = ListView.StrictlyEnforceRange + + highlightMoveDuration = 1 // This is not ideal: I would like to disable the highlight animation altogether + + // Get more messages + model.sourceModel.requestMoreMessages() + } + } } footer: RowLayout {