Verified Commit 3b3bfb18 authored by Linus Jahn's avatar Linus Jahn Committed by Jonah Brüchert

Refactor RosterDb: Load lastMessage, lastExchanged dynamically

lastMessage and lastExchanged are not required to be saved in the Roster
table. They can be found dynamically by searching for the last message in
the database.
parent dc078cb1
Pipeline #17418 passed with stages
in 76 minutes and 11 seconds
......@@ -227,6 +227,8 @@ void Database::createDbInfoTable()
void Database::createRosterTable()
{
// TODO: remove lastExchanged and lastMessage
QSqlQuery query(m_database);
Utils::execQuery(
query,
......
......@@ -83,10 +83,11 @@ Kaidan::Kaidan(QGuiApplication *app, bool enableLogging, QObject *parent)
// Load settings
//
creds.jid = m_caches->settings->value(KAIDAN_SETTINGS_AUTH_JID).toString();
creds.jidResourcePrefix = m_caches->settings->value(KAIDAN_SETTINGS_AUTH_JID_RESOURCE_PREFIX).toString();
creds.password = QString(QByteArray::fromBase64(m_caches->settings->value(
KAIDAN_SETTINGS_AUTH_PASSWD).toString().toUtf8()));
setJid(m_caches->settings->value(KAIDAN_SETTINGS_AUTH_JID).toString());
setJidResourcePrefix(m_caches->settings->value(KAIDAN_SETTINGS_AUTH_JID_RESOURCE_PREFIX).toString());
setPassword(QByteArray::fromBase64(
m_caches->settings->value(KAIDAN_SETTINGS_AUTH_PASSWD).toString().toUtf8()
));
// Use a default prefix for the JID's resource part if no prefix is already set.
if (creds.jidResourcePrefix.isEmpty())
setJidResourcePrefix(KAIDAN_JID_RESOURCE_DEFAULT_PREFIX);
......
......@@ -194,6 +194,34 @@ void MessageDb::fetchMessages(const QString &user1, const QString &user2, int in
emit messagesFetched(messages);
}
Message MessageDb::fetchLastMessage(const QString &user1, const QString &user2)
{
QSqlQuery query(QSqlDatabase::database(DB_CONNECTION));
query.setForwardOnly(true);
QMap<QString, QVariant> bindValues = {
{ QStringLiteral(":user1"), user1 },
{ QStringLiteral(":user2"), user2 },
};
Utils::execQuery(
query,
"SELECT * FROM Messages "
"WHERE (author = :user1 AND recipient = :user2) OR "
"(author = :user2 AND recipient = :user1) "
"ORDER BY timestamp DESC "
"LIMIT 1",
bindValues
);
QVector<Message> messages;
parseMessagesFromQuery(query, messages);
if (!messages.isEmpty())
return messages.first();
return {};
}
void MessageDb::addMessage(const Message &msg)
{
QSqlDatabase db = QSqlDatabase::database(DB_CONNECTION);
......
......@@ -95,6 +95,11 @@ public slots:
const QString &user2,
int index);
/**
* Fetches the last message and returns it.
*/
Message fetchLastMessage(const QString &user1, const QString &user2);
/**
* Adds a message to the database.
*/
......
......@@ -185,12 +185,8 @@ void MessageHandler::handleMessage(const QXmppMessage &msg)
message.isSpoiler() ? message.spoilerHint().isEmpty() ? tr("Spoiler")
: message.spoilerHint()
: msg.body();
emit kaidan->getRosterModel()->updateItemRequested(
contactJid,
[=] (RosterItem &item) {
item.setLastMessage(lastMessage);
}
);
emit kaidan->getRosterModel()->setLastMessageRequested(contactJid, lastMessage);
}
void MessageHandler::sendMessage(const QString& toJid,
......
......@@ -33,6 +33,9 @@
#include "Database.h"
#include "Globals.h"
#include "Utils.h"
#include "RosterItem.h"
#include "Message.h"
#include "MessageDb.h"
// Qt
#include <QDateTime>
#include <QSqlDriver>
......@@ -67,20 +70,13 @@ void RosterDb::parseItemsFromQuery(QSqlQuery &query, QVector<RosterItem> &items)
QSqlRecord rec = query.record();
int idxJid = rec.indexOf("jid");
int idxName = rec.indexOf("name");
int idxLastExchanged = rec.indexOf("lastExchanged");
int idxUnreadMessages = rec.indexOf("unreadMessages");
int idxLastMessage = rec.indexOf("lastMessage");
while (query.next()) {
RosterItem item;
item.setJid(query.value(idxJid).toString());
item.setName(query.value(idxName).toString());
item.setLastExchanged(QDateTime::fromString(
query.value(idxLastExchanged).toString(),
Qt::ISODateWithMs
));
item.setUnreadMessages(query.value(idxUnreadMessages).toInt());
item.setLastMessage(query.value(idxLastMessage).toString());
items << item;
}
......@@ -93,13 +89,6 @@ QSqlRecord RosterDb::createUpdateRecord(const RosterItem &oldItem, const RosterI
rec.append(Utils::createSqlField("jid", newItem.jid()));
if (oldItem.name() != newItem.name())
rec.append(Utils::createSqlField("name", oldItem.name()));
if (oldItem.lastMessage() != newItem.lastMessage())
rec.append(Utils::createSqlField("lastMessage", newItem.lastMessage()));
if (oldItem.lastExchanged() != newItem.lastExchanged())
rec.append(Utils::createSqlField(
"lastExchanged",
newItem.lastExchanged().toString(Qt::ISODateWithMs)
));
if (oldItem.unreadMessages() != newItem.unreadMessages())
rec.append(Utils::createSqlField(
"unreadMessages",
......@@ -129,9 +118,9 @@ void RosterDb::addItems(const QVector<RosterItem> &items)
for (const auto &item : items) {
query.addBindValue(item.jid());
query.addBindValue(item.name());
query.addBindValue(item.lastExchanged().toString(Qt::ISODateWithMs));
query.addBindValue(QStringLiteral("")); // lastExchanged (NOT NULL)
query.addBindValue(item.unreadMessages());
query.addBindValue(item.lastMessage());
query.addBindValue(QString()); // lastMessage
Utils::execQuery(query);
}
......@@ -177,6 +166,9 @@ void RosterDb::updateItem(const QString &jid,
// create an SQL record with only the differences
QSqlRecord rec = createUpdateRecord(items.first(), item);
if (rec.isEmpty())
return;
Utils::execQuery(
query,
db.driver()->sqlStatement(
......@@ -258,7 +250,7 @@ void RosterDb::clearAll()
Utils::execQuery(query, "DELETE FROM Roster");
}
void RosterDb::fetchItems()
void RosterDb::fetchItems(const QString &accountId)
{
QSqlQuery query(QSqlDatabase::database(DB_CONNECTION));
query.setForwardOnly(true);
......@@ -267,5 +259,11 @@ void RosterDb::fetchItems()
QVector<RosterItem> items;
parseItemsFromQuery(query, items);
for (auto &item : items) {
Message lastMessage = MessageDb::instance()->fetchLastMessage(accountId, item.jid());
item.setLastExchanged(lastMessage.stamp());
item.setLastMessage(lastMessage.body());
}
emit itemsFetched(items);
}
......@@ -38,7 +38,7 @@
class QSqlQuery;
class QSqlRecord;
// Kaidan
#include "RosterItem.h"
class RosterItem;
class Database;
class RosterDb : public QObject
......@@ -63,7 +63,7 @@ public:
const RosterItem &newItem);
signals:
void fetchItemsRequested();
void fetchItemsRequested(const QString &accountId);
void itemsFetched(const QVector<RosterItem> &items);
public slots:
......@@ -77,7 +77,7 @@ public slots:
void clearAll();
private slots:
void fetchItems();
void fetchItems(const QString &accountId);
private:
Database *m_db;
......
......@@ -176,19 +176,14 @@ void RosterManager::handleSendMessage(const QString &jid, const QString &message
: spoilerHint
: message;
// sorting order in contact list
const QDateTime dateTime = QDateTime::currentDateTimeUtc();
emit model->updateItemRequested(jid,
[=] (RosterItem &item) {
item.setLastMessage(lastMessage);
item.setLastExchanged(dateTime);
});
emit model->setLastExchangedRequested(jid, QDateTime::currentDateTimeUtc());
emit model->setLastMessageRequested(jid, lastMessage);
}
}
void RosterManager::handleMessage(const QXmppMessage &msg)
{
if (msg.body().isEmpty() || msg.type() == QXmppMessage::Error)
if (msg.body().isEmpty() || msg.type() == QXmppMessage::Error)
return;
// msg.from() can be our JID, if it's a carbon/forward from another client
......@@ -198,20 +193,16 @@ void RosterManager::handleMessage(const QXmppMessage &msg)
: fromJid;
// update last exchanged datetime (sorting order in contact list)
const QDateTime dateTime = QDateTime::currentDateTimeUtc();
emit model->setLastExchangedRequested(contactJid, QDateTime::currentDateTimeUtc());
// update unread message counter, if chat is not active
if (sentByMe) {
// if we sent a message (with another device), reset counter
emit model->updateItemRequested(contactJid,
[dateTime] (RosterItem &item) {
item.setLastExchanged(dateTime);
emit model->updateItemRequested(contactJid, [](RosterItem &item) {
item.setUnreadMessages(0);
});
} else if (m_chatPartner != contactJid) {
emit model->updateItemRequested(contactJid,
[dateTime] (RosterItem &item) {
item.setLastExchanged(dateTime);
emit model->updateItemRequested(contactJid, [](RosterItem &item) {
item.setUnreadMessages(item.unreadMessages() + 1);
});
}
......
......@@ -32,6 +32,7 @@
// Kaidan
#include "RosterDb.h"
#include "MessageModel.h"
#include "Kaidan.h"
// C++
#include <functional>
// Qt
......@@ -65,7 +66,20 @@ RosterModel::RosterModel(RosterDb *rosterDb, QObject *parent)
connect(this, &RosterModel::replaceItemsRequested,
rosterDb, &RosterDb::replaceItems);
emit rosterDb->fetchItemsRequested();
// This is only done in the model, the database is updated automatically by the new
// messages:
connect(this, &RosterModel::setLastMessageRequested,
this, &RosterModel::setLastMessage);
connect(this, &RosterModel::setLastExchangedRequested,
this, &RosterModel::setLastExchanged);
connect(Kaidan::instance(), &Kaidan::jidChanged, this, [=]() {
beginResetModel();
m_items.clear();
endResetModel();
emit rosterDb->fetchItemsRequested(Kaidan::instance()->getJid());
});
}
void RosterModel::setMessageModel(MessageModel *model)
......@@ -127,36 +141,13 @@ void RosterModel::handleItemsFetched(const QVector<RosterItem> &items)
{
beginResetModel();
m_items = items;
std::sort(
m_items.begin(),
m_items.end(),
[] (const RosterItem &a, const RosterItem &b) {
return a < b;
}
);
std::sort(m_items.begin(), m_items.end());
endResetModel();
}
void RosterModel::addItem(const RosterItem &item)
{
// prepend the item, if no timestamp is set
if (item.lastExchanged().isNull()) {
insertContact(0, item);
return;
}
// index where to add the new contact
int i = 0;
for (const auto &itrItem : qAsConst(m_items)) {
if (item < itrItem) {
insertContact(i, item);
return;
}
i++;
}
// append the item to the end of the list
insertContact(i, item);
insertContact(positionToInsert(item), item);
}
void RosterModel::removeItem(const QString &jid)
......@@ -188,23 +179,12 @@ void RosterModel::updateItem(const QString &jid,
if (m_items.at(i) == item)
return;
// item was changed: refresh all roles
emit dataChanged(index(i), index(i), {});
// check, if the position of the new item may be different
if (item.lastExchanged() == m_items.at(i).lastExchanged()) {
beginRemoveRows(QModelIndex(), i, i);
m_items.removeAt(i);
endRemoveRows();
// add the item at the same position
insertContact(i, item);
} else {
beginRemoveRows(QModelIndex(), i, i);
m_items.removeAt(i);
endRemoveRows();
// put to new position
addItem(item);
}
break;
updateItemPosition(i);
return;
}
}
}
......@@ -212,7 +192,7 @@ void RosterModel::updateItem(const QString &jid,
void RosterModel::replaceItems(const QHash<QString, RosterItem> &items)
{
QVector<RosterItem> newItems;
for (auto item : items) {
for (auto item : qAsConst(items)) {
// find old item
auto oldItem = std::find_if(
m_items.begin(),
......@@ -236,9 +216,63 @@ void RosterModel::replaceItems(const QHash<QString, RosterItem> &items)
handleItemsFetched(newItems);
}
void RosterModel::setLastMessage(const QString &contactJid, const QString &newLastMessage)
{
for (int i = 0; i < m_items.length(); i++) {
if (m_items.at(i).jid() == contactJid) {
m_items[i].setLastMessage(newLastMessage);
emit dataChanged(index(i), index(i), QVector<int>() << int(LastMessageRole));
return;
}
}
}
void RosterModel::setLastExchanged(const QString &contactJid, const QDateTime &newLastExchanged)
{
for (int i = 0; i < m_items.length(); i++) {
if (m_items.at(i).jid() == contactJid) {
// update item
m_items[i].setLastExchanged(newLastExchanged);
emit dataChanged(index(i), index(i), QVector<int>() << int(LastExchangedRole));
// Move row to correct position
updateItemPosition(i);
return;
}
}
}
void RosterModel::insertContact(int i, const RosterItem &item)
{
beginInsertRows(QModelIndex(), i, i);
m_items.insert(i, item);
endInsertRows();
}
int RosterModel::updateItemPosition(int currentPosition)
{
const int newPosition = positionToInsert(m_items.at(currentPosition));
if (currentPosition == newPosition)
return currentPosition;
beginMoveRows(QModelIndex(), currentPosition, currentPosition, QModelIndex(), newPosition);
m_items.move(currentPosition, newPosition);
endMoveRows();
return newPosition;
}
int RosterModel::positionToInsert(const RosterItem &item)
{
// prepend the item, if no timestamp is set
if (item.lastExchanged().isNull())
return 0;
for (int i = 0; i < m_items.size(); i++) {
if (item <= m_items.at(i))
return i;
}
// append
return m_items.size();
}
......@@ -42,6 +42,7 @@ class MessageModel;
class RosterModel : public QAbstractListModel
{
Q_OBJECT
public:
enum RosterItemRoles {
JidRole,
......@@ -66,6 +67,8 @@ signals:
void updateItemRequested(const QString &jid,
const std::function<void (RosterItem &)> &updateItem);
void replaceItemsRequested(const QHash<QString, RosterItem> &items);
void setLastMessageRequested(const QString &contactJid, const QString &newLastMessage);
void setLastExchangedRequested(const QString &contactJid, const QDateTime &newLastExchanged);
private slots:
void handleItemsFetched(const QVector<RosterItem> &items);
......@@ -75,9 +78,13 @@ private slots:
void updateItem(const QString &jid,
const std::function<void (RosterItem &)> &updateItem);
void replaceItems(const QHash<QString, RosterItem> &items);
void setLastMessage(const QString &contactJid, const QString &newLastMessage);
void setLastExchanged(const QString &contactJid, const QDateTime &newLastExchanged);
private:
void insertContact(int i, const RosterItem &item);
int updateItemPosition(int currentIndex);
int positionToInsert(const RosterItem &item);
RosterDb *rosterDb;
QVector<RosterItem> m_items;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment