Commit 2397aeb4 authored by Blue's avatar Blue 🐦 Committed by Linus Jahn

Make pending messages correctable

The message ID in the database describes the ID of the latest message
correction now (instead of the ID of original message). There's a new
column in the Messages table now for storing the 'replaceId', the ID of
the original message.

Little GUI change: Unavailable options in the message context menu are
invisible instead of being disabled.
parent b3d522da
Pipeline #28779 passed with stages
in 19 minutes and 58 seconds
......@@ -51,8 +51,8 @@
}
// Both need to be updated on version bump:
#define DATABASE_LATEST_VERSION 11
#define DATABASE_CONVERT_TO_LATEST_VERSION() DATABASE_CONVERT_TO_VERSION(11)
#define DATABASE_LATEST_VERSION 12
#define DATABASE_CONVERT_TO_LATEST_VERSION() DATABASE_CONVERT_TO_VERSION(12)
#define SQL_BOOL "BOOL"
#define SQL_INTEGER "INTEGER"
......@@ -283,6 +283,7 @@ void Database::createMessagesTable()
SQL_ATTRIBUTE(spoilerHint, SQL_TEXT)
SQL_ATTRIBUTE(isSpoiler, SQL_BOOL)
SQL_ATTRIBUTE(errorText, SQL_TEXT)
SQL_ATTRIBUTE(replaceId, SQL_TEXT)
"FOREIGN KEY(author) REFERENCES " DB_TABLE_ROSTER " (jid),"
"FOREIGN KEY(recipient) REFERENCES " DB_TABLE_ROSTER " (jid)"
)
......@@ -408,3 +409,11 @@ void Database::convertDatabaseToV11()
Utils::execQuery(query, "ALTER TABLE Messages ADD errorText " SQL_TEXT);
m_version = 11;
}
void Database::convertDatabaseToV12()
{
DATABASE_CONVERT_TO_VERSION(11);
QSqlQuery query(m_database);
Utils::execQuery(query, "ALTER TABLE Messages ADD replaceId " SQL_TEXT);
m_version = 12;
}
......@@ -110,6 +110,7 @@ private:
void convertDatabaseToV9();
void convertDatabaseToV10();
void convertDatabaseToV11();
void convertDatabaseToV12();
QSqlDatabase m_database;
......
......@@ -351,7 +351,7 @@ signals:
*
* To get/check the last message id, use `kaidan.messageModel.lastMessageId(jid)`
*/
void correctMessage(QString toJid, QString msgId, QString message);
void correctMessage(const QString &msgId, const QString &message);
/**
* Upload and send file
......
......@@ -51,7 +51,7 @@ MessageDb::MessageDb(QObject *parent)
this, &MessageDb::fetchMessages);
connect(this, &MessageDb::fetchPendingMessagesRequested,
this, &MessageDb::fetchPendingMessages);
this, &MessageDb::fetchPendingMessages);
}
MessageDb::~MessageDb()
......@@ -84,6 +84,7 @@ void MessageDb::parseMessagesFromQuery(QSqlQuery &query, QVector<Message> &msgs)
int idxSpoilerHint = rec.indexOf("spoilerHint");
int idxIsSpoiler = rec.indexOf("isSpoiler");
int idxErrorText = rec.indexOf("errorText");
int idxReplaceId = rec.indexOf("replaceId");
while (query.next()) {
Message msg;
......@@ -108,6 +109,7 @@ void MessageDb::parseMessagesFromQuery(QSqlQuery &query, QVector<Message> &msgs)
msg.setSpoilerHint(query.value(idxSpoilerHint).toString());
msg.setErrorText(query.value(idxErrorText).toString());
msg.setIsSpoiler(query.value(idxIsSpoiler).toBool());
msg.setReplaceId(query.value(idxReplaceId).toString());
msg.setReceiptRequested(true); //this is useful with resending pending messages
msgs << msg;
}
......@@ -166,6 +168,8 @@ QSqlRecord MessageDb::createUpdateRecord(const Message &oldMsg, const Message &n
rec.append(Utils::createSqlField("spoilerHint", newMsg.spoilerHint()));
if (oldMsg.isSpoiler() != newMsg.isSpoiler())
rec.append(Utils::createSqlField("isSpoiler", newMsg.isSpoiler()));
if (oldMsg.replaceId() != newMsg.replaceId())
rec.append(Utils::createSqlField("replaceId", newMsg.replaceId()));
return rec;
}
......@@ -246,6 +250,7 @@ void MessageDb::addMessage(const Message &msg)
record.setValue("mediaSize", msg.mediaSize());
record.setValue("mediaLastModified", msg.mediaLastModified().toMSecsSinceEpoch());
record.setValue("errorText", msg.errorText());
record.setValue("replaceId", msg.replaceId());
QSqlQuery query(db);
Utils::execQuery(query, db.driver()->sqlStatement(
......@@ -330,15 +335,6 @@ void MessageDb::updateMessageRecord(const QString &id,
);
}
void MessageDb::setMessageDeliveryState(const QString& msgId, Enums::DeliveryState state, const QString &errText)
{
QSqlRecord rec;
rec.append(Utils::createSqlField("deliveryState", int(state)));
rec.append(Utils::createSqlField("errorText", errText));
updateMessageRecord(msgId, rec);
}
void MessageDb::fetchPendingMessages(const QString& userJid)
{
QSqlQuery query(QSqlDatabase::database(DB_CONNECTION));
......
......@@ -84,7 +84,7 @@ signals:
void fetchPendingMessagesRequested(const QString &userJid);
/**
* Emitted, when new messages have been fetched
* Emitted when new messages have been fetched
*/
void messagesFetched(const QVector<Message> &messages);
......@@ -150,15 +150,6 @@ public slots:
void updateMessageRecord(const QString &id,
const QSqlRecord &updateRecord);
/**
* @brief Sets the message delivery state using an UPDATE query.
*
* @param msgId Message ID
* @param state Desired delivery state
*/
void setMessageDeliveryState(const QString &msgId, Enums::DeliveryState state, const QString &errText = QString());
private:
static MessageDb *s_instance;
};
......@@ -50,7 +50,7 @@ MessageHandler::MessageHandler(Kaidan *kaidan, ClientWorker *clientWorker, QXmpp
{
connect(client, &QXmppClient::messageReceived, this, &MessageHandler::handleMessage);
connect(kaidan, &Kaidan::sendMessage, this, &MessageHandler::sendMessage);
connect(kaidan, &Kaidan::correctMessage, this, &MessageHandler::correctMessage);
connect(model, &MessageModel::sendCorrectedMessageRequested, this, &MessageHandler::sendCorrectedMessage);
client->addExtension(&receiptManager);
connect(&receiptManager, &QXmppMessageReceiptManager::messageDelivered,
......@@ -220,42 +220,22 @@ void MessageHandler::sendMessage(const QString& toJid,
sendPendingMessage(msg);
}
void MessageHandler::correctMessage(const QString& toJid,
const QString& msgId,
const QString& body)
void MessageHandler::sendCorrectedMessage(const Message &msg)
{
// 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) {
auto deliveryState = Enums::DeliveryState::Sent;
QString errorText;
if (!client->sendPacket(msg)) {
// TODO store in the database only error codes, assign text messages right in the QML
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;
tr("Message correction was not successful."));
errorText = "Message correction was not successful.";
deliveryState = Enums::DeliveryState::Error;
}
Message msg;
msg.setFrom(client->configuration().jidBare());
msg.setTo(toJid);
msg.setId(QXmppUtils::generateStanzaHash(28));
msg.setBody(body);
msg.setReceiptRequested(true);
msg.setSentByMe(true);
msg.setMediaType(MessageType::MessageText); // text message without media
msg.setIsEdited(true);
msg.setReplaceId(msgId);
emit model->updateMessageRequested(msgId, [=] (Message &msg) {
msg.setBody(body);
emit model->updateMessageRequested(msg.id(), [=] (Message &localMessage) {
localMessage.setDeliveryState(deliveryState);
localMessage.setErrorText(errorText);
});
if (client->sendPacket(msg))
emit model->setMessageDeliveryStateRequested(msg.id(), Enums::DeliveryState::Sent);
else
emit kaidan->passiveNotificationRequested(
tr("Message correction was not successful."));
}
void MessageHandler::handleDiscoInfo(const QXmppDiscoveryIq &info)
......@@ -267,10 +247,22 @@ void MessageHandler::handleDiscoInfo(const QXmppDiscoveryIq &info)
carbonManager->setCarbonsEnabled(true);
}
void MessageHandler::sendPendingMessage(const Message& message)
void MessageHandler::sendPendingMessage(const Message &message)
{
if (client->state() == QXmppClient::ConnectedState) {
if (client->sendPacket(static_cast<QXmppMessage>(message)))
bool success;
// if the message is a pending edition of the existing in the history message
// I need to send it with the most recent stamp
// for that I'm gonna copy that message and update in the copy just the stamp
if (message.isEdited()) {
Message msg = message;
msg.setStamp(QDateTime::currentDateTimeUtc());
success = client->sendPacket(msg);
} else {
success = client->sendPacket(message);
}
if (success)
emit model->setMessageDeliveryStateRequested(message.id(), Enums::DeliveryState::Sent);
// TODO this "true" from sendPacket doesn't yet mean the message was successfully sent
......
......@@ -71,7 +71,7 @@ public slots:
/**
* Sends the corrected version of a message.
*/
void correctMessage(const QString& toJid, const QString& msgId, const QString& newBody);
void sendCorrectedMessage(const Message &msg);
/**
* Handles service discovery info and enables carbons if feature was found.
......@@ -84,7 +84,7 @@ private slots:
*/
void handlePendingMessages(const QVector<Message> &messages);
void sendPendingMessage(const Message& message);
void sendPendingMessage(const Message &message);
private:
Kaidan *kaidan;
......
......@@ -59,13 +59,13 @@ MessageModel::MessageModel(Kaidan *kaidan, MessageDb *msgDb, QObject *parent)
connect(this, &MessageModel::updateMessageRequested,
this, &MessageModel::updateMessage);
connect(this, &MessageModel::updateMessageRequested,
connect(this, &MessageModel::updateMessageInDatabaseRequested,
msgDb, &MessageDb::updateMessage);
connect(this, &MessageModel::setMessageDeliveryStateRequested,
this, &MessageModel::setMessageDeliveryState);
connect(this, &MessageModel::setMessageDeliveryStateRequested,
msgDb, &MessageDb::setMessageDeliveryState);
connect(kaidan, &Kaidan::correctMessage,
this, &MessageModel::correctMessage);
}
MessageModel::~MessageModel() = default;
......@@ -320,6 +320,8 @@ void MessageModel::updateMessage(const QString &id,
break;
}
}
emit updateMessageInDatabaseRequested(id, updateMsg);
}
void MessageModel::setMessageDeliveryState(const QString &msgId, Enums::DeliveryState state, const QString &errText)
......@@ -373,3 +375,43 @@ void MessageModel::sendPendingMessages()
{
emit msgDb->fetchPendingMessagesRequested(kaidan->jid());
}
void MessageModel::correctMessage(const QString &msgId, const QString &message)
{
const auto hasCorrectId = [&msgId](const Message& msg) {
return msg.id() == msgId;
};
auto itr = std::find_if(m_messages.begin(), m_messages.end(), hasCorrectId);
if (itr != m_messages.end()) {
Message &msg = *itr;
msg.setBody(message);
if (msg.deliveryState() != Enums::DeliveryState::Pending) {
msg.setId(QXmppUtils::generateStanzaHash(28));
// Set replaceId only on first correction, so it's always the original id
// (`id` is the id of the current edit, `replaceId` is the original id)
if (!msg.isEdited()) {
msg.setIsEdited(true);
msg.setReplaceId(msgId);
}
msg.setDeliveryState(Enums::DeliveryState::Pending);
if (ConnectionState(kaidan->connectionState()) == Enums::ConnectionState::StateConnected) {
// the trick with the time is important for the servers
// this way they can tell which version of the message is the latest
Message copy = msg;
copy.setStamp(QDateTime::currentDateTimeUtc());
emit sendCorrectedMessageRequested(copy);
}
} else if (!msg.isEdited()) {
msg.setStamp(QDateTime::currentDateTimeUtc());
}
QModelIndex index = createIndex(std::distance(m_messages.begin(), itr), 0);
emit dataChanged(index, index);
emit updateMessageInDatabaseRequested(msgId, [=] (Message &localMessage) {
localMessage = msg;
});
}
}
......@@ -119,6 +119,9 @@ signals:
const std::function<void (Message &)> &updateMsg);
void setMessageDeliveryStateRequested(const QString &msgId, Enums::DeliveryState state, const QString &errText = QString());
void pendingMessagesFetched(const QVector<Message> &messages);
void sendCorrectedMessageRequested(const Message &msg);
void updateMessageInDatabaseRequested(const QString &id,
const std::function<void (Message &)> &updateMsg);
private slots:
void handleMessagesFetched(const QVector<Message> &m_messages);
......@@ -128,6 +131,7 @@ private slots:
const std::function<void (Message &)> &updateMsg);
void setMessageDeliveryState(const QString &msgId, Enums::DeliveryState state, const QString &errText = QString());
void correctMessage(const QString &msgId, const QString &message);
private:
void clearAll();
......
......@@ -663,7 +663,6 @@ ChatPageBase {
)
} else if (messageField.state == "edit") {
Kaidan.correctMessage(
Kaidan.messageModel.currentChatJid,
messageToCorrect,
messageField.text
)
......
......@@ -43,7 +43,7 @@ Controls.Menu {
Controls.MenuItem {
text: qsTr("Copy message")
enabled: root.message && root.message.bodyLabel.visible
visible: root.message && root.message.bodyLabel.visible
onTriggered: {
if (root.message && !root.message.isSpoiler || message && root.message.isShowingSpoiler)
Utils.copyToClipboard(root.message && root.message.messageBody)
......@@ -60,7 +60,7 @@ Controls.Menu {
Controls.MenuItem {
text: qsTr("Copy download URL")
enabled: root.message && root.message.mediaGetUrl
visible: root.message && root.message.mediaGetUrl
onTriggered: Utils.copyToClipboard(root.message.mediaGetUrl)
}
......
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