1
0
mirror of https://github.com/qTox/qTox.git synced 2024-03-22 14:00:36 +08:00

refactor(core): strong type message receipt and row ID

Avoid implicit casting and invalid arithmetic.
This commit is contained in:
Anthony Bilinski 2019-01-27 03:52:19 -08:00
parent 8422c09f6a
commit edf6b67313
No known key found for this signature in database
GPG Key ID: 2AA8E0DA1B31FB3C
14 changed files with 144 additions and 77 deletions

View File

@ -28,6 +28,7 @@
#include "src/model/groupinvite.h" #include "src/model/groupinvite.h"
#include "src/nexus.h" #include "src/nexus.h"
#include "src/persistence/profile.h" #include "src/persistence/profile.h"
#include "src/util/strongtype.h"
#include <QCoreApplication> #include <QCoreApplication>
#include <QRegularExpression> #include <QRegularExpression>
@ -579,7 +580,7 @@ void Core::onGroupTitleChange(Tox*, uint32_t groupId, uint32_t peerId, const uin
void Core::onReadReceiptCallback(Tox*, uint32_t friendId, uint32_t receipt, void* core) void Core::onReadReceiptCallback(Tox*, uint32_t friendId, uint32_t receipt, void* core)
{ {
emit static_cast<Core*>(core)->receiptRecieved(friendId, receipt); emit static_cast<Core*>(core)->receiptRecieved(friendId, ReceiptNum{receipt});
} }
void Core::acceptFriendRequest(const ToxPk& friendPk) void Core::acceptFriendRequest(const ToxPk& friendPk)
@ -652,33 +653,35 @@ void Core::requestFriendship(const ToxId& friendId, const QString& message)
emit saveRequest(); emit saveRequest();
} }
int Core::sendMessageWithType(uint32_t friendId, const QString& message, Tox_Message_Type type) bool Core::sendMessageWithType(uint32_t friendId, const QString& message, Tox_Message_Type type, ReceiptNum& receipt)
{ {
int size = message.toUtf8().size(); int size = message.toUtf8().size();
auto maxSize = tox_max_message_length(); auto maxSize = tox_max_message_length();
if (size > maxSize) { if (size > maxSize) {
qCritical() << "Core::sendMessageWithType called with message of size:" << size << "when max is:" << maxSize <<". Ignoring."; qCritical() << "Core::sendMessageWithType called with message of size:" << size << "when max is:" << maxSize <<". Ignoring.";
return 0; return false;
} }
ToxString cMessage(message); ToxString cMessage(message);
Tox_Err_Friend_Send_Message error; Tox_Err_Friend_Send_Message error;
int receipt = tox_friend_send_message(tox.get(), friendId, type, receipt = ReceiptNum{tox_friend_send_message(tox.get(), friendId, type,
cMessage.data(), cMessage.size(), &error); cMessage.data(), cMessage.size(), &error)};
parseFriendSendMessageError(error); if (parseFriendSendMessageError(error)) {
return receipt; return true;
}
return false;
} }
int Core::sendMessage(uint32_t friendId, const QString& message) bool Core::sendMessage(uint32_t friendId, const QString& message, ReceiptNum& receipt)
{ {
QMutexLocker ml(coreLoopLock.get()); QMutexLocker ml(coreLoopLock.get());
return sendMessageWithType(friendId, message, TOX_MESSAGE_TYPE_NORMAL); return sendMessageWithType(friendId, message, TOX_MESSAGE_TYPE_NORMAL, receipt);
} }
int Core::sendAction(uint32_t friendId, const QString& action) bool Core::sendAction(uint32_t friendId, const QString& action, ReceiptNum& receipt)
{ {
QMutexLocker ml(coreLoopLock.get()); QMutexLocker ml(coreLoopLock.get());
return sendMessageWithType(friendId, action, TOX_MESSAGE_TYPE_ACTION); return sendMessageWithType(friendId, action, TOX_MESSAGE_TYPE_ACTION, receipt);
} }
void Core::sendTyping(uint32_t friendId, bool typing) void Core::sendTyping(uint32_t friendId, bool typing)

View File

@ -25,6 +25,7 @@
#include "toxid.h" #include "toxid.h"
#include "src/core/dhtserver.h" #include "src/core/dhtserver.h"
#include "src/util/strongtype.h"
#include <tox/tox.h> #include <tox/tox.h>
#include <QMutex> #include <QMutex>
@ -51,6 +52,8 @@ enum class Status
class Core; class Core;
using ToxCorePtr = std::unique_ptr<Core>; using ToxCorePtr = std::unique_ptr<Core>;
using ReceiptNum = NamedType<uint32_t, struct ReceiptNumTag>;
Q_DECLARE_METATYPE(ReceiptNum);
class Core : public QObject class Core : public QObject
{ {
@ -118,11 +121,11 @@ public slots:
void setUsername(const QString& username); void setUsername(const QString& username);
void setStatusMessage(const QString& message); void setStatusMessage(const QString& message);
int sendMessage(uint32_t friendId, const QString& message); bool sendMessage(uint32_t friendId, const QString& message, ReceiptNum& receipt);
void sendGroupMessage(int groupId, const QString& message); void sendGroupMessage(int groupId, const QString& message);
void sendGroupAction(int groupId, const QString& message); void sendGroupAction(int groupId, const QString& message);
void changeGroupTitle(int groupId, const QString& title); void changeGroupTitle(int groupId, const QString& title);
int sendAction(uint32_t friendId, const QString& action); bool sendAction(uint32_t friendId, const QString& action, ReceiptNum& receipt);
void sendTyping(uint32_t friendId, bool typing); void sendTyping(uint32_t friendId, bool typing);
void sendAvatarFile(uint32_t friendId, const QByteArray& data); void sendAvatarFile(uint32_t friendId, const QByteArray& data);
@ -200,7 +203,7 @@ signals:
void groupSentFailed(int groupId); void groupSentFailed(int groupId);
void actionSentResult(uint32_t friendId, const QString& action, int success); void actionSentResult(uint32_t friendId, const QString& action, int success);
void receiptRecieved(int friedId, int receipt); void receiptRecieved(int friedId, ReceiptNum receipt);
void failedToRemoveFriend(uint32_t friendId); void failedToRemoveFriend(uint32_t friendId);
@ -234,7 +237,7 @@ private:
static void onReadReceiptCallback(Tox* tox, uint32_t friendId, uint32_t receipt, void* core); static void onReadReceiptCallback(Tox* tox, uint32_t friendId, uint32_t receipt, void* core);
void sendGroupMessageWithType(int groupId, const QString& message, Tox_Message_Type type); void sendGroupMessageWithType(int groupId, const QString& message, Tox_Message_Type type);
int sendMessageWithType(uint32_t friendId, const QString& message, Tox_Message_Type type); bool sendMessageWithType(uint32_t friendId, const QString& message, Tox_Message_Type type, ReceiptNum& receipt);
bool parsePeerQueryError(Tox_Err_Conference_Peer_Query error) const; bool parsePeerQueryError(Tox_Err_Conference_Peer_Query error) const;
bool parseConferenceJoinError(Tox_Err_Conference_Join error) const; bool parseConferenceJoinError(Tox_Err_Conference_Join error) const;
bool checkConnection(); bool checkConnection();

View File

@ -365,6 +365,7 @@ int main(int argc, char* argv[])
QObject::connect(a.get(), &QApplication::aboutToQuit, cleanup); QObject::connect(a.get(), &QApplication::aboutToQuit, cleanup);
qRegisterMetaType<ReceiptNum>();
// Run // Run
int errorcode = a->exec(); int errorcode = a->exec();

View File

@ -645,7 +645,7 @@ void RawDatabase::process()
} }
if (query.insertCallback) if (query.insertCallback)
query.insertCallback(sqlite3_last_insert_rowid(sqlite)); query.insertCallback(RowId{sqlite3_last_insert_rowid(sqlite)});
} }
if (trans.success != nullptr) if (trans.success != nullptr)

View File

@ -13,6 +13,7 @@
#include <atomic> #include <atomic>
#include <functional> #include <functional>
#include <memory> #include <memory>
#include "src/util/strongtype.h"
/// The two following defines are required to use SQLCipher /// The two following defines are required to use SQLCipher
/// They are used by the sqlite3.h header /// They are used by the sqlite3.h header
@ -21,6 +22,8 @@
#include <sqlite3.h> #include <sqlite3.h>
using RowId = NamedType<int64_t, struct RowIdTag>;
Q_DECLARE_METATYPE(RowId);
class RawDatabase : QObject class RawDatabase : QObject
{ {
@ -31,13 +34,13 @@ public:
{ {
public: public:
Query(QString query, QVector<QByteArray> blobs = {}, Query(QString query, QVector<QByteArray> blobs = {},
const std::function<void(int64_t)>& insertCallback = {}) const std::function<void(RowId)>& insertCallback = {})
: query{query.toUtf8()} : query{query.toUtf8()}
, blobs{blobs} , blobs{blobs}
, insertCallback{insertCallback} , insertCallback{insertCallback}
{ {
} }
Query(QString query, const std::function<void(int64_t)>& insertCallback) Query(QString query, const std::function<void(RowId)>& insertCallback)
: query{query.toUtf8()} : query{query.toUtf8()}
, insertCallback{insertCallback} , insertCallback{insertCallback}
{ {
@ -52,7 +55,7 @@ public:
private: private:
QByteArray query; QByteArray query;
QVector<QByteArray> blobs; QVector<QByteArray> blobs;
std::function<void(int64_t)> insertCallback; std::function<void(RowId)> insertCallback;
std::function<void(const QVector<QVariant>&)> rowCallback; std::function<void(const QVector<QVariant>&)> rowCallback;
QVector<sqlite3_stmt*> statements; QVector<sqlite3_stmt*> statements;

View File

@ -199,7 +199,7 @@ void History::removeFriendHistory(const QString& friendPk)
QVector<RawDatabase::Query> QVector<RawDatabase::Query>
History::generateNewMessageQueries(const QString& friendPk, const QString& message, History::generateNewMessageQueries(const QString& friendPk, const QString& message,
const QString& sender, const QDateTime& time, bool isSent, const QString& sender, const QDateTime& time, bool isSent,
QString dispName, std::function<void(int64_t)> insertIdCallback) QString dispName, std::function<void(RowId)> insertIdCallback)
{ {
QVector<RawDatabase::Query> queries; QVector<RawDatabase::Query> queries;
@ -288,7 +288,7 @@ void History::onFileInsertionReady(FileDbInsertionData data)
.arg(static_cast<int>(data.direction)) .arg(static_cast<int>(data.direction))
.arg(ToxFile::CANCELED), .arg(ToxFile::CANCELED),
{data.fileId.toUtf8(), data.filePath.toUtf8(), data.fileName.toUtf8(), QByteArray()}, {data.fileId.toUtf8(), data.filePath.toUtf8(), data.fileName.toUtf8(), QByteArray()},
[weakThis, fileId](int64_t id) { [weakThis, fileId](RowId id) {
auto pThis = weakThis.lock(); auto pThis = weakThis.lock();
if (pThis) { if (pThis) {
emit pThis->fileInserted(id, fileId); emit pThis->fileInserted(id, fileId);
@ -299,12 +299,12 @@ void History::onFileInsertionReady(FileDbInsertionData data)
queries += RawDatabase::Query(QStringLiteral("UPDATE history " queries += RawDatabase::Query(QStringLiteral("UPDATE history "
"SET file_id = (last_insert_rowid()) " "SET file_id = (last_insert_rowid()) "
"WHERE id = %1") "WHERE id = %1")
.arg(data.historyId)); .arg(data.historyId.get()));
db->execLater(queries); db->execLater(queries);
} }
void History::onFileInserted(int64_t dbId, QString fileId) void History::onFileInserted(RowId dbId, QString fileId)
{ {
auto& fileInfo = fileInfos[fileId]; auto& fileInfo = fileInfos[fileId];
if (fileInfo.finished) { if (fileInfo.finished) {
@ -317,7 +317,7 @@ void History::onFileInserted(int64_t dbId, QString fileId)
} }
} }
RawDatabase::Query History::generateFileFinished(int64_t id, bool success, const QString& filePath, RawDatabase::Query History::generateFileFinished(RowId id, bool success, const QString& filePath,
const QByteArray& fileHash) const QByteArray& fileHash)
{ {
auto file_state = success ? ToxFile::FINISHED : ToxFile::CANCELED; auto file_state = success ? ToxFile::FINISHED : ToxFile::CANCELED;
@ -326,14 +326,14 @@ RawDatabase::Query History::generateFileFinished(int64_t id, bool success, const
"SET file_state = %1, file_path = ?, file_hash = ?" "SET file_state = %1, file_path = ?, file_hash = ?"
"WHERE id = %2") "WHERE id = %2")
.arg(file_state) .arg(file_state)
.arg(id), .arg(id.get()),
{filePath.toUtf8(), fileHash}); {filePath.toUtf8(), fileHash});
} else { } else {
return RawDatabase::Query(QStringLiteral("UPDATE file_transfers " return RawDatabase::Query(QStringLiteral("UPDATE file_transfers "
"SET finished = %1 " "SET finished = %1 "
"WHERE id = %2") "WHERE id = %2")
.arg(file_state) .arg(file_state)
.arg(id)); .arg(id.get()));
} }
} }
@ -370,7 +370,7 @@ void History::addNewFileMessage(const QString& friendPk, const QString& fileId,
insertionData.size = size; insertionData.size = size;
insertionData.direction = direction; insertionData.direction = direction;
auto insertFileTransferFn = [weakThis, insertionData](int64_t messageId) { auto insertFileTransferFn = [weakThis, insertionData](RowId messageId) {
auto insertionDataRw = std::move(insertionData); auto insertionDataRw = std::move(insertionData);
insertionDataRw.historyId = messageId; insertionDataRw.historyId = messageId;
@ -395,7 +395,7 @@ void History::addNewFileMessage(const QString& friendPk, const QString& fileId,
*/ */
void History::addNewMessage(const QString& friendPk, const QString& message, const QString& sender, void History::addNewMessage(const QString& friendPk, const QString& message, const QString& sender,
const QDateTime& time, bool isSent, QString dispName, const QDateTime& time, bool isSent, QString dispName,
const std::function<void(int64_t)>& insertIdCallback) const std::function<void(RowId)>& insertIdCallback)
{ {
if (!Settings::getInstance().getEnableLogging()) { if (!Settings::getInstance().getEnableLogging()) {
qWarning() << "Blocked a message from being added to database while history is disabled"; qWarning() << "Blocked a message from being added to database while history is disabled";
@ -413,7 +413,7 @@ void History::setFileFinished(const QString& fileId, bool success, const QString
const QByteArray& fileHash) const QByteArray& fileHash)
{ {
auto& fileInfo = fileInfos[fileId]; auto& fileInfo = fileInfos[fileId];
if (fileInfo.fileId == -1) { if (fileInfo.fileId.get() == -1) {
fileInfo.finished = true; fileInfo.finished = true;
fileInfo.success = success; fileInfo.success = success;
fileInfo.filePath = filePath; fileInfo.filePath = filePath;
@ -616,13 +616,13 @@ QDateTime History::getStartDateChatHistory(const QString& friendPk)
* *
* @param id Message ID. * @param id Message ID.
*/ */
void History::markAsSent(qint64 messageId) void History::markAsSent(RowId messageId)
{ {
if (!isValid()) { if (!isValid()) {
return; return;
} }
db->execLater(QString("DELETE FROM faux_offline_pending WHERE id=%1;").arg(messageId)); db->execLater(QString("DELETE FROM faux_offline_pending WHERE id=%1;").arg(messageId.get()));
} }
@ -642,7 +642,7 @@ QList<History::HistMessage> History::getChatHistory(const QString& friendPk, con
auto rowCallback = [&messages](const QVector<QVariant>& row) { auto rowCallback = [&messages](const QVector<QVariant>& row) {
// dispName and message could have null bytes, QString::fromUtf8 // dispName and message could have null bytes, QString::fromUtf8
// truncates on null bytes so we strip them // truncates on null bytes so we strip them
auto id = row[0].toLongLong(); auto id = RowId{row[0].toLongLong()};
auto isOfflineMessage = row[1].isNull(); auto isOfflineMessage = row[1].isNull();
auto timestamp = QDateTime::fromMSecsSinceEpoch(row[2].toLongLong()); auto timestamp = QDateTime::fromMSecsSinceEpoch(row[2].toLongLong());
auto friend_key = row[3].toString(); auto friend_key = row[3].toString();

View File

@ -95,7 +95,7 @@ struct FileDbInsertionData
{ {
FileDbInsertionData(); FileDbInsertionData();
int64_t historyId; RowId historyId;
QString friendPk; QString friendPk;
QString fileId; QString fileId;
QString fileName; QString fileName;
@ -111,7 +111,7 @@ class History : public QObject, public std::enable_shared_from_this<History>
public: public:
struct HistMessage struct HistMessage
{ {
HistMessage(qint64 id, bool isSent, QDateTime timestamp, QString chat, QString dispName, HistMessage(RowId id, bool isSent, QDateTime timestamp, QString chat, QString dispName,
QString sender, QString message) QString sender, QString message)
: chat{chat} : chat{chat}
, sender{sender} , sender{sender}
@ -122,7 +122,7 @@ public:
, content(std::move(message)) , content(std::move(message))
{} {}
HistMessage(qint64 id, bool isSent, QDateTime timestamp, QString chat, QString dispName, HistMessage(RowId id, bool isSent, QDateTime timestamp, QString chat, QString dispName,
QString sender, ToxFile file) QString sender, ToxFile file)
: chat{chat} : chat{chat}
, sender{sender} , sender{sender}
@ -138,7 +138,7 @@ public:
QString sender; QString sender;
QString dispName; QString dispName;
QDateTime timestamp; QDateTime timestamp;
qint64 id; RowId id;
bool isSent; bool isSent;
HistMessageContent content; HistMessageContent content;
}; };
@ -161,7 +161,7 @@ public:
void removeFriendHistory(const QString& friendPk); void removeFriendHistory(const QString& friendPk);
void addNewMessage(const QString& friendPk, const QString& message, const QString& sender, void addNewMessage(const QString& friendPk, const QString& message, const QString& sender,
const QDateTime& time, bool isSent, QString dispName, const QDateTime& time, bool isSent, QString dispName,
const std::function<void(int64_t)>& insertIdCallback = {}); const std::function<void(RowId)>& insertIdCallback = {});
void addNewFileMessage(const QString& friendPk, const QString& fileId, void addNewFileMessage(const QString& friendPk, const QString& fileId,
const QString& fileName, const QString& filePath, int64_t size, const QString& fileName, const QString& filePath, int64_t size,
@ -177,27 +177,27 @@ public:
const ParameterSearch& parameter); const ParameterSearch& parameter);
QDateTime getStartDateChatHistory(const QString& friendPk); QDateTime getStartDateChatHistory(const QString& friendPk);
void markAsSent(qint64 messageId); void markAsSent(RowId messageId);
protected: protected:
QVector<RawDatabase::Query> QVector<RawDatabase::Query>
generateNewMessageQueries(const QString& friendPk, const QString& message, generateNewMessageQueries(const QString& friendPk, const QString& message,
const QString& sender, const QDateTime& time, bool isSent, const QString& sender, const QDateTime& time, bool isSent,
QString dispName, std::function<void(int64_t)> insertIdCallback = {}); QString dispName, std::function<void(RowId)> insertIdCallback = {});
signals: signals:
void fileInsertionReady(FileDbInsertionData data); void fileInsertionReady(FileDbInsertionData data);
void fileInserted(int64_t dbId, QString fileId); void fileInserted(RowId dbId, QString fileId);
private slots: private slots:
void onFileInsertionReady(FileDbInsertionData data); void onFileInsertionReady(FileDbInsertionData data);
void onFileInserted(int64_t dbId, QString fileId); void onFileInserted(RowId dbId, QString fileId);
private: private:
QList<HistMessage> getChatHistory(const QString& friendPk, const QDateTime& from, QList<HistMessage> getChatHistory(const QString& friendPk, const QDateTime& from,
const QDateTime& to, int numMessages); const QDateTime& to, int numMessages);
static RawDatabase::Query generateFileFinished(int64_t fileId, bool success, static RawDatabase::Query generateFileFinished(RowId fileId, bool success,
const QString& filePath, const QByteArray& fileHash); const QString& filePath, const QByteArray& fileHash);
void dbSchemaUpgrade(); void dbSchemaUpgrade();
@ -211,7 +211,7 @@ private:
bool success = false; bool success = false;
QString filePath; QString filePath;
QByteArray fileHash; QByteArray fileHash;
int64_t fileId = -1; RowId fileId{-1};
}; };
// This needs to be a shared pointer to avoid callback lifetime issues // This needs to be a shared pointer to avoid callback lifetime issues

View File

@ -33,7 +33,7 @@ OfflineMsgEngine::OfflineMsgEngine(Friend* frnd)
{ {
} }
void OfflineMsgEngine::dischargeReceipt(int receipt) void OfflineMsgEngine::dischargeReceipt(ReceiptNum receipt)
{ {
QMutexLocker ml(&mutex); QMutexLocker ml(&mutex);
@ -47,14 +47,14 @@ void OfflineMsgEngine::dischargeReceipt(int receipt)
processReceipt(receipt); processReceipt(receipt);
} }
void OfflineMsgEngine::registerReceipt(int receipt, int64_t messageID, ChatMessage::Ptr msg) void OfflineMsgEngine::registerReceipt(ReceiptNum receipt, RowId messageID, ChatMessage::Ptr msg)
{ {
QMutexLocker ml(&mutex); QMutexLocker ml(&mutex);
auto it = receipts.find(receipt); auto it = receipts.find(receipt);
if (it == receipts.end()) { if (it == receipts.end()) {
it = receipts.insert(receipt, Receipt()); it = receipts.insert(receipt, Receipt());
} else if (it->bRowValid && receipt != 0 /* offline receipt */) { } else if (it->bRowValid && receipt.get() != 0 /* offline receipt */) {
qWarning() << "Received duplicate registration of receipt"; qWarning() << "Received duplicate registration of receipt";
} }
it->rowId = messageID; it->rowId = messageID;
@ -76,7 +76,7 @@ void OfflineMsgEngine::deliverOfflineMsgs()
if (undeliveredMsgs.size() == 0) if (undeliveredMsgs.size() == 0)
return; return;
QMap<int64_t, MsgPtr> msgs = undeliveredMsgs; QMap<RowId, MsgPtr> msgs = undeliveredMsgs;
removeAllReceipts(); removeAllReceipts();
undeliveredMsgs.clear(); undeliveredMsgs.clear();
@ -84,11 +84,11 @@ void OfflineMsgEngine::deliverOfflineMsgs()
auto val = iter.value(); auto val = iter.value();
auto key = iter.key(); auto key = iter.key();
QString messageText = val.msg->toString(); QString messageText = val.msg->toString();
int rec; ReceiptNum rec;
if (val.msg->isAction()) { if (val.msg->isAction()) {
rec = Core::getInstance()->sendAction(f->getId(), messageText); Core::getInstance()->sendAction(f->getId(), messageText, rec);
} else { } else {
rec = Core::getInstance()->sendMessage(f->getId(), messageText); Core::getInstance()->sendMessage(f->getId(), messageText, rec);
} }
registerReceipt(rec, key, val.msg); registerReceipt(rec, key, val.msg);
@ -102,7 +102,7 @@ void OfflineMsgEngine::removeAllReceipts()
receipts.clear(); receipts.clear();
} }
void OfflineMsgEngine::updateTimestamp(int receiptId) void OfflineMsgEngine::updateTimestamp(ReceiptNum receiptId)
{ {
QMutexLocker ml(&mutex); QMutexLocker ml(&mutex);
@ -118,7 +118,7 @@ void OfflineMsgEngine::updateTimestamp(int receiptId)
receipts.erase(receipt); receipts.erase(receipt);
} }
void OfflineMsgEngine::processReceipt(int receiptId) void OfflineMsgEngine::processReceipt(ReceiptNum receiptId)
{ {
const auto receipt = receipts.constFind(receiptId); const auto receipt = receipts.constFind(receiptId);
if (receipt == receipts.end()) { if (receipt == receipts.end()) {
@ -138,6 +138,6 @@ void OfflineMsgEngine::processReceipt(int receiptId)
if (QThread::currentThread() == QCoreApplication::instance()->thread()) { if (QThread::currentThread() == QCoreApplication::instance()->thread()) {
updateTimestamp(receiptId); updateTimestamp(receiptId);
} else { } else {
QMetaObject::invokeMethod(this, "updateTimestamp", Qt::QueuedConnection, Q_ARG(int, receiptId)); QMetaObject::invokeMethod(this, "updateTimestamp", Qt::QueuedConnection, Q_ARG(ReceiptNum, receiptId));
} }
} }

View File

@ -21,6 +21,8 @@
#define OFFLINEMSGENGINE_H #define OFFLINEMSGENGINE_H
#include "src/chatlog/chatmessage.h" #include "src/chatlog/chatmessage.h"
#include "src/core/core.h"
#include "src/persistence/db/rawdatabase.h"
#include <QDateTime> #include <QDateTime>
#include <QMap> #include <QMap>
#include <QMutex> #include <QMutex>
@ -36,32 +38,32 @@ public:
explicit OfflineMsgEngine(Friend*); explicit OfflineMsgEngine(Friend*);
virtual ~OfflineMsgEngine() = default; virtual ~OfflineMsgEngine() = default;
void dischargeReceipt(int receipt); void dischargeReceipt(ReceiptNum receipt);
void registerReceipt(int receipt, int64_t messageID, ChatMessage::Ptr msg); void registerReceipt(ReceiptNum receipt, RowId messageID, ChatMessage::Ptr msg);
void deliverOfflineMsgs(); void deliverOfflineMsgs();
public slots: public slots:
void removeAllReceipts(); void removeAllReceipts();
void updateTimestamp(int receiptId); void updateTimestamp(ReceiptNum receiptId);
private: private:
void processReceipt(int receiptId); void processReceipt(ReceiptNum receiptId);
struct Receipt struct Receipt
{ {
bool bRowValid{false}; bool bRowValid{false};
int64_t rowId{0}; RowId rowId{0};
bool bRecepitReceived{false}; bool bRecepitReceived{false};
}; };
struct MsgPtr struct MsgPtr
{ {
ChatMessage::Ptr msg; ChatMessage::Ptr msg;
int receipt; ReceiptNum receipt;
}; };
QMutex mutex; QMutex mutex;
Friend* f; Friend* f;
QHash<int, Receipt> receipts; QHash<ReceiptNum, Receipt> receipts;
QMap<int64_t, MsgPtr> undeliveredMsgs; QMap<RowId, MsgPtr> undeliveredMsgs;
static const int offlineTimeout; static const int offlineTimeout;
}; };

55
src/util/strongtype.h Normal file
View File

@ -0,0 +1,55 @@
/*
Copyright © 2019 by The qTox Project Contributors
This file is part of qTox, a Qt-based graphical interface for Tox.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
qTox is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with qTox. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef STORNGTYPE_H
#define STORNGTYPE_H
#include <QHash>
/* This class facilitates creating a named class which wraps underlying POD,
* avoiding implict casts and arithmetic of the underlying data.
* Usage: Declare named type with arbitrary tag, then hook up Qt metatype for use
* in signals/slots. For queued connections, registering the metatype is also
* required before the type is used.
* using ReceiptNum = NamedType<uint32_t, struct ReceiptNumTag>;
* Q_DECLARE_METATYPE(ReceiptNum);
* qRegisterMetaType<ReceiptNum>();
*/
template <typename T, typename Parameter>
class NamedType
{
public:
NamedType() {}
explicit NamedType(T const& value) : value_(value) {}
T& get() { return value_; }
T const& get() const {return value_; }
bool operator==(const NamedType& rhs) const { return value_ == rhs.value_; }
bool operator<(const NamedType& rhs) const { return value_ < rhs.value_; }
bool operator>(const NamedType& rhs) const { return value_ > rhs.value_; }
private:
T value_;
};
template <typename T, typename Parameter>
inline uint qHash(const NamedType<T,Parameter> &key, uint seed = 0) {
return qHash(key.get(), seed);
}
#endif // STORNGTYPE_H

View File

@ -686,7 +686,7 @@ void ChatForm::onStatusMessage(const QString& message)
} }
} }
void ChatForm::onReceiptReceived(quint32 friendId, int receipt) void ChatForm::onReceiptReceived(quint32 friendId, ReceiptNum receipt)
{ {
if (friendId == f->getId()) { if (friendId == f->getId()) {
offlineEngine->dischargeReceipt(receipt); offlineEngine->dischargeReceipt(receipt);
@ -885,7 +885,7 @@ ChatForm::MessageMetadata ChatForm::getMessageMetadata(History::HistMessage cons
const bool isAction = const bool isAction =
histMessage.content.getType() == HistMessageContentType::message histMessage.content.getType() == HistMessageContentType::message
&& histMessage.content.asMessage().startsWith(ACTION_PREFIX, Qt::CaseInsensitive); && histMessage.content.asMessage().startsWith(ACTION_PREFIX, Qt::CaseInsensitive);
const qint64 id = histMessage.id; const RowId id = histMessage.id;
return {isSelf, needSending, isAction, id, authorPk, msgDateTime}; return {isSelf, needSending, isAction, id, authorPk, msgDateTime};
} }
@ -931,13 +931,13 @@ void ChatForm::sendLoadedMessage(ChatMessage::Ptr chatMsg, MessageMetadata const
return; return;
} }
int receipt = 0; ReceiptNum receipt{0};
if (f->getStatus() != Status::Offline) { if (f->getStatus() != Status::Offline) {
Core* core = Core::getInstance(); Core* core = Core::getInstance();
uint32_t friendId = f->getId(); uint32_t friendId = f->getId();
QString stringMsg = chatMsg->toString(); QString stringMsg = chatMsg->toString();
receipt = metadata.isAction ? core->sendAction(friendId, stringMsg) metadata.isAction ? core->sendAction(friendId, stringMsg, receipt)
: core->sendMessage(friendId, stringMsg); : core->sendMessage(friendId, stringMsg, receipt);
} }
getOfflineMsgEngine()->registerReceipt(receipt, metadata.id, chatMsg); getOfflineMsgEngine()->registerReceipt(receipt, metadata.id, chatMsg);
} }
@ -1122,11 +1122,11 @@ void ChatForm::SendMessageStr(QString msg)
historyPart = ACTION_PREFIX + part; historyPart = ACTION_PREFIX + part;
} }
int rec = 0; ReceiptNum receipt{0};
if (f->getStatus() != Status::Offline) { if (f->getStatus() != Status::Offline) {
Core* core = Core::getInstance(); Core* core = Core::getInstance();
uint32_t friendId = f->getId(); uint32_t friendId = f->getId();
rec = isAction ? core->sendAction(friendId, part) : core->sendMessage(friendId, part); isAction ? core->sendAction(friendId, part, receipt) : core->sendMessage(friendId, part, receipt);
} }
ChatMessage::Ptr ma = createSelfMessage(part, timestamp, isAction, false); ChatMessage::Ptr ma = createSelfMessage(part, timestamp, isAction, false);
@ -1138,8 +1138,8 @@ void ChatForm::SendMessageStr(QString msg)
QString name = Core::getInstance()->getUsername(); QString name = Core::getInstance()->getUsername();
bool isSent = !Settings::getInstance().getFauxOfflineMessaging(); bool isSent = !Settings::getInstance().getFauxOfflineMessaging();
history->addNewMessage(pk, historyPart, selfPk, timestamp, isSent, name, history->addNewMessage(pk, historyPart, selfPk, timestamp, isSent, name,
[offMsgEngine, rec, ma](int64_t id) { [offMsgEngine, receipt, ma](RowId id) {
offMsgEngine->registerReceipt(rec, id, ma); offMsgEngine->registerReceipt(receipt, id, ma);
}); });
} else { } else {
// TODO: Make faux-offline messaging work partially with the history disabled // TODO: Make faux-offline messaging work partially with the history disabled

View File

@ -103,7 +103,7 @@ private slots:
void onFriendNameChanged(const QString& name); void onFriendNameChanged(const QString& name);
void onFriendMessageReceived(quint32 friendId, const QString& message, bool isAction); void onFriendMessageReceived(quint32 friendId, const QString& message, bool isAction);
void onStatusMessage(const QString& message); void onStatusMessage(const QString& message);
void onReceiptReceived(quint32 friendId, int receipt); void onReceiptReceived(quint32 friendId, ReceiptNum receipt);
void onLoadHistory(); void onLoadHistory();
void onUpdateTime(); void onUpdateTime();
void sendImage(const QPixmap& pixmap); void sendImage(const QPixmap& pixmap);
@ -117,10 +117,10 @@ private:
const bool isSelf; const bool isSelf;
const bool needSending; const bool needSending;
const bool isAction; const bool isAction;
const qint64 id; const RowId id;
const ToxPk authorPk; const ToxPk authorPk;
const QDateTime msgDateTime; const QDateTime msgDateTime;
MessageMetadata(bool isSelf, bool needSending, bool isAction, qint64 id, ToxPk authorPk, MessageMetadata(bool isSelf, bool needSending, bool isAction, RowId id, ToxPk authorPk,
QDateTime msgDateTime) QDateTime msgDateTime)
: isSelf{isSelf} : isSelf{isSelf}
, needSending{needSending} , needSending{needSending}

View File

@ -240,7 +240,7 @@ void Widget::init()
connect(updateCheck.get(), &UpdateCheck::updateAvailable, this, &Widget::onUpdateAvailable); connect(updateCheck.get(), &UpdateCheck::updateAvailable, this, &Widget::onUpdateAvailable);
#endif #endif
settingsWidget = new SettingsWidget(updateCheck.get(), this); settingsWidget = new SettingsWidget(updateCheck.get(), this);
#if UPDATE_CHECK_ENABLED #if UPDATE_CHECK_ENABLED
updateCheck->checkForUpdate(); updateCheck->checkForUpdate();
#endif #endif
@ -1013,7 +1013,7 @@ void Widget::addFriend(uint32_t friendId, const ToxPk& friendPk)
connect(widget, &FriendWidget::copyFriendIdToClipboard, this, &Widget::copyFriendIdToClipboard); connect(widget, &FriendWidget::copyFriendIdToClipboard, this, &Widget::copyFriendIdToClipboard);
connect(widget, &FriendWidget::contextMenuCalled, widget, &FriendWidget::onContextMenuCalled); connect(widget, &FriendWidget::contextMenuCalled, widget, &FriendWidget::onContextMenuCalled);
connect(widget, SIGNAL(removeFriend(int)), this, SLOT(removeFriend(int))); connect(widget, SIGNAL(removeFriend(int)), this, SLOT(removeFriend(int)));
Profile* profile = Nexus::getProfile(); Profile* profile = Nexus::getProfile();
connect(profile, &Profile::friendAvatarSet, widget, &FriendWidget::onAvatarSet); connect(profile, &Profile::friendAvatarSet, widget, &FriendWidget::onAvatarSet);
connect(profile, &Profile::friendAvatarRemoved, widget, &FriendWidget::onAvatarRemoved); connect(profile, &Profile::friendAvatarRemoved, widget, &FriendWidget::onAvatarRemoved);
@ -1218,7 +1218,7 @@ void Widget::onFriendMessageReceived(int friendId, const QString& message, bool
newFriendMessageAlert(friendId); newFriendMessageAlert(friendId);
} }
void Widget::onReceiptRecieved(int friendId, int receipt) void Widget::onReceiptRecieved(int friendId, ReceiptNum receipt)
{ {
Friend* f = FriendList::findFriend(friendId); Friend* f = FriendList::findFriend(friendId);
if (!f) { if (!f) {

View File

@ -167,7 +167,7 @@ public slots:
void onFriendMessageReceived(int friendId, const QString& message, bool isAction); void onFriendMessageReceived(int friendId, const QString& message, bool isAction);
void onFriendRequestReceived(const ToxPk& friendPk, const QString& message); void onFriendRequestReceived(const ToxPk& friendPk, const QString& message);
void updateFriendActivity(const Friend* frnd); void updateFriendActivity(const Friend* frnd);
void onReceiptRecieved(int friendId, int receipt); void onReceiptRecieved(int friendId, ReceiptNum receipt);
void onEmptyGroupCreated(int groupId, const QString& title); void onEmptyGroupCreated(int groupId, const QString& title);
void onGroupInviteReceived(const GroupInvite& inviteInfo); void onGroupInviteReceived(const GroupInvite& inviteInfo);
void onGroupInviteAccepted(const GroupInvite& inviteInfo); void onGroupInviteAccepted(const GroupInvite& inviteInfo);