Commit 45e512f0 authored by Linus Jahn's avatar Linus Jahn Committed by GitHub

Count unread messages and save them in the db (#92)

Clean up all includes and don't use "Swiften/Swiften.h" anymore.
This will speed up builds much.
parent a0059808
......@@ -39,7 +39,7 @@
<name>GlobalDrawer</name>
<message>
<source>Add Contact</source>
<translation>Kontakt Hinzufügen</translation>
<translation type="vanished">Kontakt Hinzufügen</translation>
</message>
<message>
<source>Logout</source>
......@@ -49,6 +49,10 @@
<source>About</source>
<translation>Über</translation>
</message>
<message>
<source>Add new contact</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>LoginPage</name>
......@@ -101,7 +105,7 @@
<name>RosterAddContactSheet</name>
<message>
<source>Add Contact</source>
<translation>Kontakt Hinzufügen</translation>
<translation type="vanished">Kontakt Hinzufügen</translation>
</message>
<message>
<source>Nickname:</source>
......@@ -123,6 +127,10 @@
<source>Add</source>
<translation>Hinzufügen</translation>
</message>
<message>
<source>Add new contact</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>RosterPage</name>
......@@ -131,6 +139,25 @@
<translation>Kontakte</translation>
</message>
</context>
<context>
<name>RosterRemoveContactSheet</name>
<message>
<source>Do you really want to delete the contact &quot;%1&quot; from your roster?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Delete contact</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Cancel</source>
<translation type="unfinished">Abbrechen</translation>
</message>
<message>
<source>Delete</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>main</name>
<message>
......
......@@ -38,15 +38,15 @@
<context>
<name>GlobalDrawer</name>
<message>
<source>Add Contact</source>
<source>Logout</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Logout</source>
<source>About</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>About</source>
<source>Add new contact</source>
<translation type="unfinished"></translation>
</message>
</context>
......@@ -95,10 +95,6 @@
</context>
<context>
<name>RosterAddContactSheet</name>
<message>
<source>Add Contact</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Nickname:</source>
<translation type="unfinished"></translation>
......@@ -119,6 +115,10 @@
<source>Add</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Add new contact</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>RosterPage</name>
......@@ -127,4 +127,23 @@
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>RosterRemoveContactSheet</name>
<message>
<source>Do you really want to delete the contact &quot;%1&quot; from your roster?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Delete contact</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Cancel</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Delete</source>
<translation type="unfinished"></translation>
</message>
</context>
</TS>
......@@ -38,15 +38,15 @@
<context>
<name>GlobalDrawer</name>
<message>
<source>Add Contact</source>
<source>Logout</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Logout</source>
<source>About</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>About</source>
<source>Add new contact</source>
<translation type="unfinished"></translation>
</message>
</context>
......@@ -99,10 +99,6 @@
</context>
<context>
<name>RosterAddContactSheet</name>
<message>
<source>Add Contact</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Nickname:</source>
<translation type="unfinished"></translation>
......@@ -123,6 +119,10 @@
<source>Add</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Add new contact</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>RosterPage</name>
......@@ -131,4 +131,23 @@
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>RosterRemoveContactSheet</name>
<message>
<source>Do you really want to delete the contact &quot;%1&quot; from your roster?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Delete contact</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Cancel</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Delete</source>
<translation type="unfinished"></translation>
</message>
</context>
</TS>
......@@ -37,10 +37,6 @@
</context>
<context>
<name>GlobalDrawer</name>
<message>
<source>Add Contact</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Logout</source>
<translation type="unfinished">Выйти</translation>
......@@ -49,6 +45,10 @@
<source>About</source>
<translation type="unfinished">О программе</translation>
</message>
<message>
<source>Add new contact</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>LoginPage</name>
......@@ -99,10 +99,6 @@
</context>
<context>
<name>RosterAddContactSheet</name>
<message>
<source>Add Contact</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Nickname:</source>
<translation type="unfinished"></translation>
......@@ -123,6 +119,10 @@
<source>Add</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Add new contact</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>RosterPage</name>
......@@ -131,6 +131,25 @@
<translation>Контакты</translation>
</message>
</context>
<context>
<name>RosterRemoveContactSheet</name>
<message>
<source>Do you really want to delete the contact &quot;%1&quot; from your roster?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Delete contact</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Cancel</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Delete</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>main</name>
<message>
......
......@@ -31,12 +31,15 @@
// Boost
#include <boost/bind.hpp>
// Swiften
#include <Swiften/Swiften.h>
#include <Swiften/Client/Client.h>
#include <Swiften/Client/MemoryStorages.h>
#include <Swiften/Crypto/PlatformCryptoProvider.h>
#include <Swiften/Queries/Responders/SoftwareVersionResponder.h>
// Kaidan
#include "RosterController.h"
#include "PresenceController.h"
#include "MessageController.h"
#include "MessageModel.h"
#include "VCardController.h"
#include "ServiceDiscoveryManager.h"
......@@ -69,7 +72,7 @@ Kaidan::Kaidan(Swift::NetworkFactories* networkFactories, QObject *parent) : QOb
// create/update the full jid
updateFullJid();
// load controlers
// load controllers
messageController = new MessageController(&jid);
rosterController = new RosterController();
presenceController = new PresenceController();
......@@ -130,7 +133,7 @@ void Kaidan::mainConnect()
Swift::ClientOptions options;
options.useStreamCompression = false;
// .. and connect!
client->connect(options);
}
......
......@@ -28,7 +28,8 @@
#include <QString>
#include <QStringList>
// Swiften
#include <Swiften/Swiften.h>
#include <Swiften/Client/Client.h>
#include <Swiften/Client/ClientXMLTracer.h>
// Kaidan
#include "RosterController.h"
#include "MessageController.h"
......
......@@ -32,7 +32,11 @@
#include <boost/optional.hpp>
#include <boost/smart_ptr/make_shared.hpp>
// Swiften
#include <Swiften/Swiften.h>
#include <Swiften/Client/Client.h>
#include <Swiften/Elements/Message.h>
#include <Swiften/Elements/DeliveryReceipt.h>
#include <Swiften/Elements/DeliveryReceiptRequest.h>
#include <Swiften/Base/IDGenerator.h>
// Kaidan
#include "MessageModel.h"
#include "Notifications.h"
......@@ -67,6 +71,7 @@ MessageModel* MessageController::getMessageModel()
void MessageController::setRecipient(QString recipient_)
{
// update the recipient
if (recipient == recipient_)
return;
......@@ -77,6 +82,9 @@ void MessageController::setRecipient(QString recipient_)
// we're offline or we haven't connected already.
messageModel->applyRecipientFilter(recipient, *ownJid);
emit messageModelChanged();
// reset the unread message counter for this contact
rosterController->resetUnreadMessagesForJid(recipient_);
}
QString MessageController::getRecipient()
......@@ -84,11 +92,6 @@ QString MessageController::getRecipient()
return recipient;
}
void MessageController::setMessageAsRead(const QString msgId)
{
messageModel->setMessageAsRead(msgId);
}
void MessageController::handleMessageReceived(Swift::Message::ref message_)
{
boost::optional<std::string> bodyOpt = message_->getBody();
......@@ -100,7 +103,7 @@ void MessageController::handleMessageReceived(Swift::Message::ref message_)
//
// author is only the 'bare' JID: e.g. 'albert@einstein.ch'
const QString author = QString(message_->getFrom().toBare().toString().c_str());
const QString author = QString::fromStdString(message_->getFrom().toBare().toString());
const QString author_resource = QString(message_->getFrom().getResource().c_str());
const QString recipient_resource = QString::fromStdString(client->getJID().getResource());
QString timestamp = QDateTime::currentDateTime().toString(Qt::ISODate); // fallback timestamp
......@@ -162,11 +165,20 @@ void MessageController::handleMessageReceived(Swift::Message::ref message_)
}
//
// Update lastExchanged in roster controller
// Update last exchanged, unread message count in roster controller
//
rosterController->updateLastExchangedOfJid(QString::fromStdString(
message_->getFrom().toBare().toString()));
if (bodyOpt)
{
const QString msgAuthor = QString::fromStdString(message_->getFrom().toBare().toString());
rosterController->updateLastExchangedOfJid(msgAuthor);
if (msgAuthor != this->recipient)
{
rosterController->newUnreadMessageForJid(msgAuthor);
}
}
}
void MessageController::sendMessage(const QString recipient_, const QString message_)
......
......@@ -24,7 +24,8 @@
#include <QObject>
#include <QSqlTableModel>
// Swiften
#include <Swiften/Swiften.h>
#include <Swiften/Client/Client.h>
#include <Swiften/Elements/Message.h>
// Kaidan
#include "MessageModel.h"
#include "RosterController.h"
......@@ -47,7 +48,6 @@ public:
QString getRecipient();
Q_INVOKABLE void sendMessage(const QString recipient_, const QString message_);
Q_INVOKABLE void setMessageAsRead(const QString msgId);
signals:
void messageModelChanged();
......
......@@ -47,20 +47,17 @@ static void createTable()
"'timestamp' TEXT NOT NULL,"
"'message' TEXT NOT NULL,"
"'id' TEXT NOT NULL,"
"'isSent' BOOL,"
"'isDelivered' BOOL,"
"'isRead' BOOL,"
"'meta' TEXT," // placeholder field for later additons
"FOREIGN KEY('author') REFERENCES Contacts ('jid'),"
"FOREIGN KEY('recipient') REFERENCES Contacts ('jid')"
"'isSent' BOOL," // is sent to server
"'isDelivered' BOOL," // message has arrived at other client
"FOREIGN KEY('author') REFERENCES Roster ('jid'),"
"FOREIGN KEY('recipient') REFERENCES Roster ('jid')"
")"))
{
qFatal("Failed to query database: %s", qPrintable(query.lastError().text()));
}
}
MessageModel::MessageModel(QObject *parent) :
QSqlTableModel(parent)
MessageModel::MessageModel(QObject *parent) : QSqlTableModel(parent)
{
createTable();
setTable(conversationsTableName);
......@@ -92,19 +89,12 @@ QVariant MessageModel::data(const QModelIndex &index, int role) const
QHash<int, QByteArray> MessageModel::roleNames() const
{
QHash<int, QByteArray> names;
names[Qt::UserRole] = "author";
names[Qt::UserRole + 1] = "author_resource";
names[Qt::UserRole + 2] = "recipient";
names[Qt::UserRole + 3] = "recipient_resource";
names[Qt::UserRole + 4] = "timestamp";
names[Qt::UserRole + 5] = "message";
names[Qt::UserRole + 6] = "id";
names[Qt::UserRole + 7] = "isSent"; // sent to server
names[Qt::UserRole + 8] = "isDelivered"; // message has arrived on a client
names[Qt::UserRole + 9] = "isRead"; // message has been read from the recipient
names[Qt::UserRole + 10] = "meta";
return names;
QHash<int, QByteArray> roles;
// record() returns an empty QSqlRecord
for (int i = 0; i < this->record().count(); i++) {
roles.insert(Qt::UserRole + i, record().fieldName(i).toUtf8());
}
return roles;
}
void MessageModel::setMessageAsSent(const QString msgId)
......@@ -121,13 +111,6 @@ void MessageModel::setMessageAsDelivered(const QString msgId)
submitAll();
}
void MessageModel::setMessageAsRead(const QString msgId)
{
QSqlQuery newQuery;
newQuery.exec(QString("UPDATE 'Messages' SET 'isRead' = 1 WHERE id = '%1'").arg(msgId));
submitAll();
}
void MessageModel::addMessage(const QString* author, const QString* author_resource,
const QString* recipient, const QString* recipient_resource,
const QString* timestamp, const QString* message, const QString* msgId,
......@@ -137,19 +120,20 @@ void MessageModel::addMessage(const QString* author, const QString* author_resou
// add the new message
//
QSqlRecord newRecord = record();
newRecord.setValue("author", *author);
newRecord.setValue("author_resource", *author_resource);
newRecord.setValue("recipient", *recipient);
newRecord.setValue("recipient_resource", *recipient_resource);
newRecord.setValue("timestamp", *timestamp);
newRecord.setValue("message", *message);
newRecord.setValue("id", *msgId);
newRecord.setValue("isSent", sentByMe ? false : true);
newRecord.setValue("isDelivered", sentByMe ? false : true);
newRecord.setValue("isRead", sentByMe ? true : false);
if (!insertRecord(rowCount(), newRecord)) {
QSqlRecord record = this->record();
record.setValue("author", *author);
record.setValue("author_resource", *author_resource);
record.setValue("recipient", *recipient);
record.setValue("recipient_resource", *recipient_resource);
record.setValue("timestamp", *timestamp);
record.setValue("message", *message);
record.setValue("id", *msgId);
record.setValue("isSent", sentByMe ? false : true);
record.setValue("isDelivered", sentByMe ? false : true);
record.setValue("isRead", sentByMe ? false : true);
record.setValue("isReadByMe", sentByMe ? true : false);
if (!insertRecord(rowCount(), record)) {
qWarning() << "Failed to add message to DB:" << lastError().text();
return;
}
......
......@@ -39,7 +39,6 @@ public:
const QString* msgId, bool sentByMe);
void setMessageAsSent(const QString msgId);
void setMessageAsDelivered(const QString msgId);
void setMessageAsRead(const QString msgId);
signals:
void recipientChanged();
......
......@@ -19,7 +19,8 @@
#include "PresenceController.h"
#include <QObject>
#include <Swiften/Swiften.h>
#include <Swiften/Client/Client.h>
#include <Swiften/Elements/Presence.h>
#include <boost/bind.hpp>
PresenceController::PresenceController(QObject* parent) : QObject(parent)
......
......@@ -21,7 +21,8 @@
#define PRESENCECONTROLLER_H
#include <QObject>
#include <Swiften/Swiften.h>
#include <Swiften/Client/Client.h>
#include <Swiften/Elements/Presence.h>
class PresenceController : public QObject
{
......
......@@ -20,7 +20,8 @@
// RosterController
#include "RosterController.h"
// C++
// Std
#include <iostream>
#include <string.h>
// Qt 5
#include <QQmlContext>
......@@ -32,7 +33,13 @@
// Kaidan
#include "RosterModel.h"
// Swiften
#include <Swiften/Swiften.h>
#include <Swiften/Base/IDGenerator.h>
#include <Swiften/Client/Client.h>
#include <Swiften/Elements/ErrorPayload.h>
#include <Swiften/JID/JID.h>
#include <Swiften/Roster/GetRosterRequest.h>
#include <Swiften/Roster/XMPPRoster.h>
#include <Swiften/Queries/IQRouter.h>
// Boost
#include <boost/bind.hpp>
......@@ -237,3 +244,19 @@ void RosterController::updateLastExchangedOfJid(const QString jid_)
// send signal for updating the GUI
emit rosterModelChanged();
}
void RosterController::newUnreadMessageForJid(const QString jid_)
{
// get the current unread message count
int msgCount = rosterModel->getUnreadMessageCountOfJid(&jid_);
// increase it by one
msgCount++;
// set the new increased count
rosterModel->setUnreadMessageCountOfJid(&jid_, msgCount);
}
void RosterController::resetUnreadMessagesForJid(const QString jid_)
{
rosterModel->setUnreadMessageCountOfJid(&jid_, 0);
emit rosterModelChanged();
}
......@@ -26,7 +26,8 @@
#include <QSqlTableModel>
#include <QQmlListProperty>
// Swiften
#include <Swiften/Swiften.h>
#include <Swiften/Client/Client.h>
#include <Swiften/Elements/ErrorPayload.h>
// Kaidan
#include "RosterModel.h"
......@@ -43,6 +44,8 @@ public:
void requestRosterFromClient();
RosterModel* getRosterModel();
void updateLastExchangedOfJid(const QString jid_);
void newUnreadMessageForJid(const QString jid_);
void resetUnreadMessagesForJid(const QString jid_);
Q_INVOKABLE void addContact(const QString jid_, const QString name_);
Q_INVOKABLE void removeContact(const QString);
......@@ -57,7 +60,7 @@ private:
void handleJidUpdated(const Swift::JID &jid_, const std::string &name_, const std::vector<std::string>&);
void handleRosterCleared();
Swift::Client* client;
Swift::IQRouter *iqRouter;
Swift::IQRouter* iqRouter;
Swift::XMPPRoster* xmppRoster;
RosterModel* rosterModel;
};
......
......@@ -40,12 +40,12 @@ static void createTable()
"'jid' TEXT NOT NULL,"
"'name' TEXT NOT NULL,"
"'lastExchanged' TEXT NOT NULL,"
"'unreadMessages' INTEGER," // < UNUSED v
"'lastMessage' TEXT,"
"'unreadMessages' INTEGER,"
"'lastMessage' TEXT," // < UNUSED v
"'lastOnline' TEXT,"
"'activity' TEXT,"
"'status' TEXT,"
"'mood' TEXT" // < UNUSED ^
"'mood' TEXT" // < UNUSED ^
")"))
{
qFatal("Failed to query database: %s", qPrintable(query.lastError().text()));
......@@ -138,7 +138,7 @@ QStringList RosterModel::getJidList()
QStringList retVar;
QSqlQuery query;
query.exec("SELECT * FROM 'Roster'");
query.exec("SELECT jid FROM Roster");
int jidCol = query.record().indexOf("jid");
......@@ -164,3 +164,35 @@ void RosterModel::setLastExchangedOfJid(const QString jid_, const QString date_)
newQuery.exec(QString("UPDATE 'Roster' SET lastExchanged = '%1' WHERE jid = '%2'").arg(date_, jid_));
submitAll();
}
int RosterModel::getUnreadMessageCountOfJid(const QString* jid_)
{
QSqlQuery query;
query.prepare(QString("SELECT unreadMessages FROM Roster WHERE jid = '%1'").arg(*jid_));
if (!query.exec())
{
qDebug("Failed to query database: %s", qPrintable(query.lastError().text()));
return 0;
}
query.next();
return query.value("unreadMessages").toInt();
}
void RosterModel::setUnreadMessageCountOfJid(const QString* jid_, const int count_)
{