Commit 4da78fbc authored by Linus Jahn's avatar Linus Jahn 🍙

Implement XEP-0308: Last Message Correction (back-end)

parent dfe6860c
......@@ -158,6 +158,10 @@
<source>Could not send message, as a result of not being connected.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Could not correct message, as a result of not being connected.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>RosterManager</name>
......
......@@ -295,6 +295,10 @@
<source>Could not send message, as a result of not being connected.</source>
<translation>Konnte Nachricht nicht versenden, da Sie nicht verbunden sind.</translation>
</message>
<message>
<source>Could not correct message, as a result of not being connected.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>RosterAddContactSheet</name>
......
......@@ -158,6 +158,10 @@
<source>Could not send message, as a result of not being connected.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Could not correct message, as a result of not being connected.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>RosterManager</name>
......
......@@ -267,6 +267,10 @@
<source>Could not send message, as a result of not being connected.</source>
<translation>No se pudo enviar el mensaje porque no está conectado.</translation>
</message>
<message>
<source>Could not correct message, as a result of not being connected.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>RosterAddContactSheet</name>
......
......@@ -224,6 +224,10 @@
<source>Could not send message, as a result of not being connected.</source>
<translation type="unfinished">Ezin izan da mezua bidali, konektaturik ez zaudelako.</translation>
</message>
<message>
<source>Could not correct message, as a result of not being connected.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>RosterAddContactSheet</name>
......
......@@ -274,6 +274,10 @@
<source>Could not send message, as a result of not being connected.</source>
<translation type="unfinished">Vous n&apos;êtes pas connecté. Impossible d&apos;envoyer le message</translation>
</message>
<message>
<source>Could not correct message, as a result of not being connected.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>RosterAddContactSheet</name>
......
......@@ -158,6 +158,10 @@
<source>Could not send message, as a result of not being connected.</source>
<translation>Impossibile inviare messaggi a causa della mancata connessione.</translation>
</message>
<message>
<source>Could not correct message, as a result of not being connected.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>RosterManager</name>
......
......@@ -185,6 +185,10 @@
<source>Could not send message, as a result of not being connected.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Could not correct message, as a result of not being connected.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>RosterAddContactSheet</name>
......
......@@ -270,6 +270,10 @@
<source>Could not send message, as a result of not being connected.</source>
<translation>Tidak dapat hantar mesej, kerana anda tiada sambungan internet.</translation>
</message>
<message>
<source>Could not correct message, as a result of not being connected.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>RosterAddContactSheet</name>
......
......@@ -251,6 +251,10 @@
<source>Could not send message, as a result of not being connected.</source>
<translation type="unfinished">Kunne ikke sende melding, som følge av manglende tilkobling.</translation>
</message>
<message>
<source>Could not correct message, as a result of not being connected.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>RosterAddContactSheet</name>
......
......@@ -214,6 +214,10 @@
<source>Could not send message, as a result of not being connected.</source>
<translation>Kan geen bericht verzenden, omdat er geen verbinding is gemaakt.</translation>
</message>
<message>
<source>Could not correct message, as a result of not being connected.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>RosterAddContactSheet</name>
......
......@@ -158,6 +158,10 @@
<source>Could not send message, as a result of not being connected.</source>
<translation>Nie można było wysłać wiadomości, w wyniku braku połączenia.</translation>
</message>
<message>
<source>Could not correct message, as a result of not being connected.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>RosterManager</name>
......
......@@ -206,6 +206,10 @@
<source>Could not send message, as a result of not being connected.</source>
<translation>Não foi possível enviar mensagem, como resultado de não estar conectado.</translation>
</message>
<message>
<source>Could not correct message, as a result of not being connected.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>RosterManager</name>
......
......@@ -248,6 +248,10 @@
<source>Could not send message, as a result of not being connected.</source>
<translation>Не смог отправить сообщение, в результате того, что не был подключен.</translation>
</message>
<message>
<source>Could not correct message, as a result of not being connected.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>RosterAddContactSheet</name>
......
......@@ -41,7 +41,7 @@
#include <QSqlQuery>
#include <QSqlRecord>
static const int DATABASE_LATEST_VERSION = 8;
static const int DATABASE_LATEST_VERSION = 9;
static const char *DATABASE_TABLE_INFO = "dbinfo";
static const char *DATABASE_TABLE_MESSAGES = "Messages";
static const char *DATABASE_TABLE_ROSTER = "Roster";
......@@ -124,7 +124,7 @@ bool Database::needToConvert()
void Database::convertDatabase()
{
qDebug() << "[database] Converting database to latest version from verion" << version;
qDebug() << "[database] Converting database to latest version from version" << version;
while (version < DATABASE_LATEST_VERSION) {
switch (version) {
case 0:
......@@ -143,6 +143,8 @@ void Database::convertDatabase()
convertDatabaseToV7(); version = 7; break;
case 7:
convertDatabaseToV8(); version = 8; break;
case 8:
convertDatabaseToV9(); version = 9; break;
default:
break;
}
......@@ -161,7 +163,7 @@ void Database::convertDatabase()
void Database::createNewDatabase()
{
QSqlQuery query(database);
//
// DB info
//
......@@ -205,6 +207,7 @@ void Database::createNewDatabase()
"'mediaLocation' TEXT,"
"'mediaThumb' BLOB,"
"'mediaHashes' TEXT,"
"'edited' BOOL," // whether the message has been edited
"FOREIGN KEY('author') REFERENCES Roster ('jid'),"
"FOREIGN KEY('recipient') REFERENCES Roster ('jid')"
")"
......@@ -219,7 +222,7 @@ void Database::createDbInfoTable()
QSqlQuery query(database);
query.prepare("CREATE TABLE IF NOT EXISTS 'dbinfo' (version INTEGER NOT NULL)");
execQuery(query);
query.prepare(QString("INSERT INTO 'dbinfo' (version) VALUES (%1)")
.arg(DATABASE_LATEST_VERSION));
execQuery(query);
......@@ -328,6 +331,14 @@ void Database::convertDatabaseToV8()
execQuery(query);
}
void Database::convertDatabaseToV9()
{
QSqlQuery query(database);
query.prepare("ALTER TABLE 'Messages' ADD 'edited' BOOL");
execQuery(query);
}
void Database::execQuery(QSqlQuery &query)
{
if (!query.exec()) {
......
......@@ -60,6 +60,7 @@ private:
void convertDatabaseToV6();
void convertDatabaseToV7();
void convertDatabaseToV8();
void convertDatabaseToV9();
void execQuery(QSqlQuery &query);
QSqlDatabase database;
......
......@@ -359,6 +359,13 @@ signals:
*/
void sendMessage(QString jid, QString message);
/**
* Correct the last message
*
* To get/check the last message id, use `kaidan.messageModel.lastMessageId(jid)`
*/
void correctMessage(QString toJid, QString msgId, QString message);
/**
* Upload and send file
*/
......
......@@ -53,6 +53,7 @@ MessageHandler::MessageHandler(Kaidan *kaidan, QXmppClient *client, MessageModel
{
connect(client, &QXmppClient::messageReceived, this, &MessageHandler::handleMessage);
connect(kaidan, &Kaidan::sendMessage, this, &MessageHandler::sendMessage);
connect(kaidan, &Kaidan::correctMessage, this, &MessageHandler::correctMessage);
client->addExtension(&receiptManager);
connect(&receiptManager, &QXmppMessageReceiptManager::messageDelivered,
......@@ -140,7 +141,18 @@ void MessageHandler::handleMessage(const QXmppMessage &msg)
entry.timestamp = stamp.toUTC().toString(Qt::ISODate);
// save the message to the database
#if QXMPP_VERSION >= QT_VERSION_CHECK(1, 0, 0)
// in case of message correction, replace old message
if (msg.replaceId().isEmpty()) {
emit model->addMessageRequested(entry);
} else {
entry.edited = true;
emit model->updateMessageRequested(msg.replaceId(), entry);
}
#else
// no message correction with old QXmpp
emit model->addMessageRequested(entry);
#endif
// Send a message notification
//
......@@ -184,8 +196,49 @@ void MessageHandler::sendMessage(QString toJid, QString body)
m.setId(msg.id);
m.setReceiptRequested(true);
// TODO: check return code
client->sendPacket(m);
bool success = client->sendPacket(m);
if (success)
emit model->setMessageAsSentRequested(msg.id);
// TODO: handle error
}
void MessageHandler::correctMessage(QString toJid, QString msgId, QString body)
{
// TODO: load old message from model and put everything into the new message
// instead of only the new body
// TODO: Add offline message cache and send when connnected again
if (client->state() != QXmppClient::ConnectedState) {
emit kaidan->passiveNotificationRequested(
tr("Could not correct message, as a result of not being connected.")
);
qWarning() << "[client] [MessageHandler] Could not correct message, as a result of "
"not being connected.";
return;
}
MessageModel::Message msg;
msg.author = client->configuration().jidBare();
msg.recipient = toJid;
msg.id = QXmppUtils::generateStanzaHash(48);
msg.sentByMe = true;
msg.message = body;
msg.type = MessageType::MessageText; // text message without media
msg.edited = true;
emit model->updateMessageRequested(msgId, msg);
QXmppMessage m(msg.author, msg.recipient, body);
m.setId(msg.id);
m.setReceiptRequested(true);
#if QXMPP_VERSION >= QT_VERSION_CHECK(1, 0, 0)
m.setReplaceId(msgId);
#endif
bool success = client->sendPacket(m);
if (success)
emit model->setMessageAsSentRequested(msg.id);
// TODO: handle error
}
void MessageHandler::handleDiscoInfo(const QXmppDiscoveryIq &info)
......
......@@ -74,6 +74,11 @@ public slots:
*/
void sendMessage(QString toJid, QString body);
/**
* Sends the corrected version of a message
*/
void correctMessage(QString toJid, QString msgId, QString newBody);
/**
* Handles service discovery info and enables carbons if feature was found
*/
......
......@@ -107,6 +107,11 @@ MessageType MessageModel::messageTypeFromMimeType(const QMimeType &type)
return MessageType::MessageFile;
}
QString MessageModel::lastMessageId(QString jid) const
{
return lastMsgIdCache.value(jid, "");
}
void MessageModel::setMessageAsSent(const QString msgId)
{
for (int i = 0; i < rowCount(); ++i) {
......@@ -150,12 +155,15 @@ void MessageModel::updateMessage(const QString id, Message msg)
if (!found)
return;
QString newId = msg.id.isEmpty() ? id : msg.id;
rec.setValue("id", newId);
rec.setValue("edited", msg.edited);
rec.setValue("isSent", msg.isSent);
rec.setValue("isDelivered", msg.isDelivered);
if (!msg.timestamp.isEmpty())
rec.setValue("timestamp", msg.timestamp);
if (!msg.message.isEmpty())
rec.setValue("message", msg.message);
if (!msg.id.isEmpty())
rec.setValue("id", msg.id);
if (!msg.mediaUrl.isEmpty())
rec.setValue("mediaUrl", msg.mediaUrl);
if (msg.mediaSize)
......@@ -173,6 +181,12 @@ void MessageModel::updateMessage(const QString id, Message msg)
setRecord(recId, rec);
submitAll();
// check if we're author/recipient
QVariant jid = rec.value("author").toString() == ownJid ? rec.value("recipient")
: rec.value("author");
// update last message id
lastMsgIdCache[jid.toString()] = newId;
}
void MessageModel::addMessage(Message msg)
......@@ -187,9 +201,10 @@ void MessageModel::addMessage(Message msg)
record.setValue("timestamp", msg.timestamp);
record.setValue("message", msg.message);
record.setValue("id", msg.id);
record.setValue("isSent", !msg.sentByMe);
record.setValue("isDelivered", !msg.sentByMe);
record.setValue("isSent", msg.isSent);
record.setValue("isDelivered", msg.isDelivered);
record.setValue("type", (quint8) msg.type);
record.setValue("edited", msg.edited);
record.setValue("mediaUrl", msg.mediaUrl);
if (msg.mediaSize)
record.setValue("mediaSize", msg.mediaSize);
......@@ -206,4 +221,11 @@ void MessageModel::addMessage(Message msg)
}
submitAll();
// update last message id
if (!msg.id.isEmpty()) {
// check if we're author/recipient
QString jid = msg.author == ownJid ? msg.recipient : msg.author;
lastMsgIdCache[jid] = msg.id;
}
}
......@@ -63,6 +63,9 @@ public:
QString id;
bool sentByMe;
MessageType type;
bool edited = false;
bool isSent = false;
bool isDelivered = false;
QString mediaUrl;
quint64 mediaSize;
QString mediaContentType;
......@@ -74,6 +77,14 @@ public:
static MessageType messageTypeFromMimeType(const QMimeType &);
/**
* Returns the last message id of a contact
*
* The result can be empty, if the last message was sent in a previous session. This
* is, because we currently can't be sure if there were other messages since then.
*/
Q_INVOKABLE QString lastMessageId(QString jid) const;
signals:
/**
* Emitted when the user opens another chat to apply a filter to the db
......@@ -103,6 +114,8 @@ private:
QSqlDatabase *database;
QString ownJid;
QHash<QString, QString> lastMsgIdCache;
};
#endif // MESSAGEMODEL_H
......@@ -129,8 +129,10 @@ void UploadManager::handleUploadSucceeded(const QXmppHttpUpload *upload)
m.setOutOfBandUrl(upload->slot().getUrl().toEncoded());
#endif
// TODO: check return code
client->sendPacket(m);
bool success = client->sendPacket(m);
if (success)
emit msgModel->setMessageAsSentRequested(originalMsg->id);
// TODO: handle error
messages.remove(upload->id());
}
......
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