diff --git a/src/core.cpp b/src/core.cpp index f3134974b..2f8211519 100644 --- a/src/core.cpp +++ b/src/core.cpp @@ -268,6 +268,7 @@ void Core::start() tox_callback_file_data(tox, onFileDataCallback, this); tox_callback_avatar_info(tox, onAvatarInfoCallback, this); tox_callback_avatar_data(tox, onAvatarDataCallback, this); + tox_callback_read_receipt(tox, onReadReceiptCallback, this); toxav_register_callstate_callback(toxav, onAvInvite, av_OnInvite, this); toxav_register_callstate_callback(toxav, onAvStart, av_OnStart, this); @@ -700,6 +701,11 @@ void Core::onAvatarDataCallback(Tox*, int32_t friendnumber, uint8_t, } } +void Core::onReadReceiptCallback(Tox*, int32_t friendnumber, uint32_t receipt, void *core) +{ + emit static_cast(core)->receiptRecieved(friendnumber, receipt); +} + void Core::acceptFriendRequest(const QString& userId) { int friendId = tox_add_friend_norequest(tox, CUserId(userId).data()); @@ -739,23 +745,22 @@ void Core::requestFriendship(const QString& friendAddress, const QString& messag saveConfiguration(); } -void Core::sendMessage(int friendId, const QString& message) +int Core::sendMessage(int friendId, const QString& message) { - QList cMessages = splitMessage(message); - - for (auto &cMsg :cMessages) - { - int messageId = tox_send_message(tox, friendId, cMsg.data(), cMsg.size()); - if (messageId == 0) - emit messageSentResult(friendId, message, messageId); - } + QMutexLocker ml(&messageSendMutex); + CString cMessage(message); + int receipt = tox_send_message(tox, friendId, cMessage.data(), cMessage.size()); + emit messageSentResult(friendId, message, receipt); + return receipt; } -void Core::sendAction(int friendId, const QString &action) +int Core::sendAction(int friendId, const QString &action) { + QMutexLocker ml(&messageSendMutex); CString cMessage(action); - int ret = tox_send_action(tox, friendId, cMessage.data(), cMessage.size()); - emit actionSentResult(friendId, action, ret); + int receipt = tox_send_action(tox, friendId, cMessage.data(), cMessage.size()); + emit messageSentResult(friendId, action, receipt); + return receipt; } void Core::sendTyping(int friendId, bool typing) diff --git a/src/core.h b/src/core.h index 1c439025a..8fe15de03 100644 --- a/src/core.h +++ b/src/core.h @@ -45,6 +45,7 @@ public: static const QString TOX_EXT; static const QString CONFIG_FILE_NAME; static QString sanitize(QString name); + static QList splitMessage(const QString &message); QString getPeerName(const ToxID& id) const; @@ -92,10 +93,10 @@ public slots: void setStatusMessage(const QString& message); void setAvatar(uint8_t format, const QByteArray& data); - void sendMessage(int friendId, const QString& message); + int sendMessage(int friendId, const QString& message); void sendGroupMessage(int groupId, const QString& message); void sendGroupAction(int groupId, const QString& message); - void sendAction(int friendId, const QString& action); + int sendAction(int friendId, const QString& action); void sendTyping(int friendId, bool typing); void sendFile(int32_t friendId, QString Filename, QString FilePath, long long filesize); @@ -154,7 +155,8 @@ signals: void messageSentResult(int friendId, const QString& message, int messageId); void groupSentResult(int groupId, const QString& message, int result); - void actionSentResult(int friendId, const QString& action, int success); + + void receiptRecieved(int friedId, int receipt); void failedToAddFriend(const QString& userId, const QString& errorInfo = QString()); void failedToRemoveFriend(int friendId); @@ -215,6 +217,7 @@ private: static void onFileDataCallback(Tox *tox, int32_t friendnumber, uint8_t filenumber, const uint8_t *data, uint16_t length, void *userdata); static void onAvatarInfoCallback(Tox* tox, int32_t friendnumber, uint8_t format, uint8_t *hash, void *userdata); static void onAvatarDataCallback(Tox* tox, int32_t friendnumber, uint8_t format, uint8_t *hash, uint8_t *data, uint32_t datalen, void *userdata); + static void onReadReceiptCallback(Tox *tox, int32_t friendnumber, uint32_t receipt, void *core); static void onAvInvite(void* toxav, int32_t call_index, void* core); static void onAvStart(void* toxav, int32_t call_index, void* core); @@ -247,8 +250,6 @@ private: void checkLastOnline(int friendId); - QList splitMessage(const QString &message); - private slots: void onFileTransferFinished(ToxFile file); @@ -262,7 +263,7 @@ private: int dhtServerId; static QList fileSendQueue, fileRecvQueue; static ToxCall calls[]; - QMutex fileSendMutex; + QMutex fileSendMutex, messageSendMutex; bool ready; uint8_t* pwsaltedkeys[PasswordType::ptCounter]; // use the pw's hash as the "pw" diff --git a/src/historykeeper.cpp b/src/historykeeper.cpp index 4dc3335af..84fb4cf7c 100644 --- a/src/historykeeper.cpp +++ b/src/historykeeper.cpp @@ -37,9 +37,10 @@ HistoryKeeper *HistoryKeeper::getInstance() { QList initLst; initLst.push_back(QString("CREATE TABLE IF NOT EXISTS history (id INTEGER PRIMARY KEY AUTOINCREMENT, timestamp INTEGER NOT NULL, ") + - QString("chat_id INTEGER NOT NULL, sender INTEGER NOT NULL, sent_ok BOOLEAN NOT NULL DEFAULT TRUE, message TEXT NOT NULL);")); + QString("chat_id INTEGER NOT NULL, sender INTEGER NOT NULL, message TEXT NOT NULL);")); initLst.push_back(QString("CREATE TABLE IF NOT EXISTS aliases (id INTEGER PRIMARY KEY AUTOINCREMENT, user_id TEXT UNIQUE NOT NULL);")); initLst.push_back(QString("CREATE TABLE IF NOT EXISTS chats (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT UNIQUE NOT NULL, ctype INTEGER NOT NULL);")); + initLst.push_back(QString("CREATE TABLE IF NOT EXISTS sent_status (id INTEGER PRIMARY KEY AUTOINCREMENT, status INTEGER NOT NULL DEFAULT 0);")); QString path(":memory:"); GenericDdInterface *dbIntf; @@ -107,8 +108,31 @@ HistoryKeeper::HistoryKeeper(GenericDdInterface *db_) : message */ + // for old tables: + QSqlQuery ans = db->exec("select seq from sqlite_sequence where name=\"history\";"); + if (ans.first()) + { + QSqlQuery ret = db->exec("select seq from sqlite_sequence where name=\"sent_status\";"); + int idCur = 0; + if (ret.first()) + { + idCur = ret.value(0).toInt(); + } + + int idMax = ans.value(0).toInt(); + for (int i = idCur; i < idMax; i++) + { + QString cmd = QString("INSERT INTO sent_status (status) VALUES (1)"); + db->exec(cmd); + } + } + updateChatsID(); updateAliases(); + + QSqlQuery sqlAnswer = db->exec("select seq from sqlite_sequence where name=\"history\";"); + sqlAnswer.first(); + messageID = sqlAnswer.value(0).toInt(); } HistoryKeeper::~HistoryKeeper() @@ -116,7 +140,7 @@ HistoryKeeper::~HistoryKeeper() delete db; } -void HistoryKeeper::addChatEntry(const QString& chat, const QString& message, const QString& sender, const QDateTime &dt) +int HistoryKeeper::addChatEntry(const QString& chat, const QString& message, const QString& sender, const QDateTime &dt, bool isSent) { int chat_id = getChatID(chat, ctSingle).first; int sender_id = getAliasID(sender); @@ -124,6 +148,10 @@ void HistoryKeeper::addChatEntry(const QString& chat, const QString& message, co db->exec(QString("INSERT INTO history (timestamp, chat_id, sender, message)") + QString("VALUES (%1, %2, %3, '%4');") .arg(dt.toMSecsSinceEpoch()).arg(chat_id).arg(sender_id).arg(wrapMessage(message))); + db->exec(QString("INSERT INTO sent_status (status) VALUES (%1)").arg(isSent)); + + messageID++; + return messageID; } QList HistoryKeeper::getChatHistory(HistoryKeeper::ChatType ct, const QString &chat, @@ -139,8 +167,8 @@ QList HistoryKeeper::getChatHistory(HistoryKeeper::C QSqlQuery dbAnswer; if (ct == ctSingle) { - dbAnswer = db->exec(QString("SELECT timestamp, user_id, message FROM history INNER JOIN aliases ON history.sender = aliases.id ") + - QString("AND timestamp BETWEEN %1 AND %2 AND chat_id = %3;") + dbAnswer = db->exec(QString("SELECT history.id, timestamp, user_id, message, status FROM history INNER JOIN aliases ON history.sender = aliases.id ") + + QString("INNER JOIN sent_status ON history.id = sent_status.id AND timestamp BETWEEN %1 AND %2 AND chat_id = %3;") .arg(time64_from).arg(time64_to).arg(chat_id)); } else { // no groupchats yet @@ -148,12 +176,17 @@ QList HistoryKeeper::getChatHistory(HistoryKeeper::C while (dbAnswer.next()) { - QString sender = dbAnswer.value(1).toString(); - QString message = unWrapMessage(dbAnswer.value(2).toString()); - qint64 timeInt = dbAnswer.value(0).toLongLong(); + qint64 id = dbAnswer.value(0).toLongLong(); + qint64 timeInt = dbAnswer.value(1).toLongLong(); + QString sender = dbAnswer.value(2).toString(); + QString message = unWrapMessage(dbAnswer.value(3).toString()); + bool isSent = true; + if (!dbAnswer.value(4).isNull()) + isSent = dbAnswer.value(4).toBool(); + QDateTime time = QDateTime::fromMSecsSinceEpoch(timeInt); - res.push_back({sender,message,time}); + res.push_back({id, sender,message,time,isSent}); } return res; @@ -235,13 +268,15 @@ void HistoryKeeper::resetInstance() historyInstance = nullptr; } -void HistoryKeeper::addGroupChatEntry(const QString &chat, const QString &message, const QString &sender, const QDateTime &dt) +int HistoryKeeper::addGroupChatEntry(const QString &chat, const QString &message, const QString &sender, const QDateTime &dt) { Q_UNUSED(chat) Q_UNUSED(message) Q_UNUSED(sender) Q_UNUSED(dt) // no groupchats yet + + return -1; } HistoryKeeper::ChatType HistoryKeeper::convertToChatType(int ct) @@ -275,3 +310,8 @@ void HistoryKeeper::renameHistory(QString from, QString to) if (filePlain.exists()) filePlain.rename(QDir(Settings::getInstance().getSettingsDirPath()).filePath(to + ".qtox_history")); } + +void HistoryKeeper::markAsSent(int m_id) +{ + db->exec(QString("UPDATE sent_status SET status = 1 WHERE id = %1;").arg(m_id)); +} diff --git a/src/historykeeper.h b/src/historykeeper.h index bfe3208c4..e2d1b8b02 100644 --- a/src/historykeeper.h +++ b/src/historykeeper.h @@ -30,9 +30,11 @@ public: struct HistMessage { + qint64 id; QString sender; QString message; QDateTime timestamp; + bool isSent; }; virtual ~HistoryKeeper(); @@ -44,9 +46,10 @@ public: static bool checkPassword(); static void renameHistory(QString from, QString to); - void addChatEntry(const QString& chat, const QString& message, const QString& sender, const QDateTime &dt); - void addGroupChatEntry(const QString& chat, const QString& message, const QString& sender, const QDateTime &dt); + int addChatEntry(const QString& chat, const QString& message, const QString& sender, const QDateTime &dt, bool isSent); + int addGroupChatEntry(const QString& chat, const QString& message, const QString& sender, const QDateTime &dt); QList getChatHistory(ChatType ct, const QString &chat, const QDateTime &time_from, const QDateTime &time_to); + void markAsSent(int m_id); private: HistoryKeeper(GenericDdInterface *db_); @@ -66,6 +69,7 @@ private: QMap aliases; QMap> chats; bool isEncrypted; + int messageID; }; #endif // HISTORYKEEPER_H diff --git a/src/misc/settings.cpp b/src/misc/settings.cpp index caa2493dd..887e3b5fd 100644 --- a/src/misc/settings.cpp +++ b/src/misc/settings.cpp @@ -121,6 +121,7 @@ void Settings::load() autoAwayTime = s.value("autoAwayTime", 10).toInt(); checkUpdates = s.value("checkUpdates", false).toBool(); showInFront = s.value("showInFront", false).toBool(); + fauxOfflineMessaging = s.value("fauxOfflineMessaging", false).toBool(); s.endGroup(); s.beginGroup("Widgets"); @@ -259,6 +260,7 @@ void Settings::save(QString path) s.setValue("autoAwayTime", autoAwayTime); s.setValue("checkUpdates", checkUpdates); s.setValue("showInFront", showInFront); + s.setValue("fauxOfflineMessaging", fauxOfflineMessaging); s.endGroup(); s.beginGroup("Widgets"); @@ -849,3 +851,13 @@ void Settings::setFriendAlias(const ToxID &id, const QString &alias) it->alias = alias; } } + +bool Settings::getFauxOfflineMessaging() const +{ + return fauxOfflineMessaging; +} + +void Settings::setFauxOfflineMessaging(bool value) +{ + fauxOfflineMessaging = value; +} diff --git a/src/misc/settings.h b/src/misc/settings.h index 15131e34a..36143de26 100644 --- a/src/misc/settings.h +++ b/src/misc/settings.h @@ -199,6 +199,9 @@ public: QString getFriendAlias(const ToxID &id) const; void setFriendAlias(const ToxID &id, const QString &alias); + bool getFauxOfflineMessaging() const; + void setFauxOfflineMessaging(bool value); + public: void save(); void save(QString path); @@ -218,6 +221,7 @@ private: int dhtServerId; bool dontShowDhtDialog; + bool fauxOfflineMessaging; bool enableIPv6; QString translation; static bool makeToxPortable; @@ -281,6 +285,7 @@ private: QHash friendLst; + signals: //void dataChanged(); void dhtServerListChanged(); diff --git a/src/widget/chatareawidget.cpp b/src/widget/chatareawidget.cpp index 142d0fb4b..393a01c0e 100644 --- a/src/widget/chatareawidget.cpp +++ b/src/widget/chatareawidget.cpp @@ -102,6 +102,10 @@ void ChatAreaWidget::insertMessage(ChatActionPtr msgAction, QTextCursor::MoveOpe checkSlider(); QTextTable *chatTextTable = getMsgTable(pos); + msgAction->assignPlace(chatTextTable, this); + msgAction->dispaly(); + + /* QTextCursor cur = chatTextTable->cellAt(0, 2).firstCursorPosition(); cur.clearSelection(); cur.setKeepPositionOnInsert(true); @@ -110,8 +114,8 @@ void ChatAreaWidget::insertMessage(ChatActionPtr msgAction, QTextCursor::MoveOpe chatTextTable->cellAt(0, 2).firstCursorPosition().insertHtml(msgAction->getMessage()); chatTextTable->cellAt(0, 4).firstCursorPosition().setBlockFormat(dateFormat); chatTextTable->cellAt(0, 4).firstCursorPosition().insertHtml(msgAction->getDate()); - msgAction->setup(cur, this); + */ if (msgAction->isInteractive()) messages.append(msgAction); diff --git a/src/widget/form/chatform.cpp b/src/widget/form/chatform.cpp index 6d0ab0be3..9850e4c36 100644 --- a/src/widget/form/chatform.cpp +++ b/src/widget/form/chatform.cpp @@ -39,6 +39,7 @@ #include "src/widget/croppinglabel.h" #include "src/misc/style.h" #include "src/misc/settings.h" +#include "src/misc/cstring.h" ChatForm::ChatForm(Friend* chatFriend) : f(chatFriend) @@ -76,11 +77,12 @@ ChatForm::ChatForm(Friend* chatFriend) connect(volButton, SIGNAL(clicked()), this, SLOT(onVolMuteToggle())); connect(chatWidget, &ChatAreaWidget::onFileTranfertInterract, this, &ChatForm::onFileTansBtnClicked); connect(Core::getInstance(), &Core::fileSendFailed, this, &ChatForm::onFileSendFailed); + connect(this, SIGNAL(chatAreaCleared()), this, SLOT(clearReciepts())); setAcceptDrops(true); if (Settings::getInstance().getEnableLogging()) - loadHistory(QDateTime::currentDateTime().addDays(-7)); + loadHistory(QDateTime::currentDateTime().addDays(-7), true); } ChatForm::~ChatForm() @@ -100,20 +102,36 @@ void ChatForm::onSendTriggered() if (msg.isEmpty()) return; - QDateTime timestamp = QDateTime::currentDateTime(); - HistoryKeeper::getInstance()->addChatEntry(f->getToxID().publicKey, msg, Core::getInstance()->getSelfId().publicKey, timestamp); + bool isAction = msg.startsWith("/me "); + if (isAction) + msg = msg = msg.right(msg.length() - 4); - if (msg.startsWith("/me ")) + QList splittedMsg = Core::splitMessage(msg); + QDateTime timestamp = QDateTime::currentDateTime(); + + for (CString& c_msg : splittedMsg) { - msg = msg.right(msg.length() - 4); - addSelfMessage(msg, true, timestamp); - emit sendAction(f->getFriendID(), msg); - } - else - { - addSelfMessage(msg, false, timestamp); - emit sendMessage(f->getFriendID(), msg); + QString qt_msg = CString::toString(c_msg.data(), c_msg.size()); + QString qt_msg_hist = qt_msg; + if (isAction) + qt_msg_hist = "/me " + qt_msg; + + bool status = !Settings::getInstance().getFauxOfflineMessaging(); + + int id = HistoryKeeper::getInstance()->addChatEntry(f->getToxID().publicKey, qt_msg_hist, + Core::getInstance()->getSelfId().publicKey, timestamp, status); + + MessageActionPtr ma = addSelfMessage(msg, isAction, timestamp, false); + + int rec; + if (isAction) + rec = Core::getInstance()->sendAction(f->getFriendID(), msg); + else + rec = Core::getInstance()->sendMessage(f->getFriendID(), msg); + + registerReceipt(rec, id, ma); } + msgEdit->clear(); } @@ -691,7 +709,7 @@ void ChatForm::onAvatarRemoved(int FriendId) avatar->setPixmap(QPixmap(":/img/contact_dark.png"), Qt::transparent); } -void ChatForm::loadHistory(QDateTime since) +void ChatForm::loadHistory(QDateTime since, bool processUndelivered) { QDateTime now = QDateTime::currentDateTime(); @@ -728,7 +746,21 @@ void ChatForm::loadHistory(QDateTime since) } // Show each messages - ChatActionPtr ca = genMessageActionAction(ToxID::fromString(it.sender), it.message, false, msgDateTime); + MessageActionPtr ca = genMessageActionAction(ToxID::fromString(it.sender), it.message, false, msgDateTime); + if (it.isSent) + { + ca->markAsSent(); + } else { + if (processUndelivered) + { + int rec; + if (ca->isAction()) + rec = Core::getInstance()->sendAction(f->getFriendID(), ca->getRawMessage()); + else + rec = Core::getInstance()->sendMessage(f->getFriendID(), ca->getRawMessage()); + registerReceipt(rec, it.id, ca); + } + } historyMessages.append(ca); } std::swap(storedPrevId, previousId); @@ -787,7 +819,6 @@ void ChatForm::updateTime() callDuration->setText(secondsToDHMS(timeElapsed.elapsed()/1000)); } - QString ChatForm::secondsToDHMS(quint32 duration) { QString res; @@ -810,3 +841,53 @@ QString ChatForm::secondsToDHMS(quint32 duration) //I assume no one will ever have call longer than ~30days return cD + res.sprintf("%dd%02dh %02dm %02ds", days, hours, minutes, seconds); } + +void ChatForm::registerReceipt(int receipt, int messageID, MessageActionPtr msg) +{ + receipts[receipt] = messageID; + undeliveredMsgs[messageID] = msg; +} + +void ChatForm::dischargeReceipt(int receipt) +{ + auto it = receipts.find(receipt); + if (it != receipts.end()) + { + int mID = it.value(); + auto msgIt = undeliveredMsgs.find(mID); + if (msgIt != undeliveredMsgs.end()) + { + HistoryKeeper::getInstance()->markAsSent(mID); + msgIt.value()->markAsSent(); + msgIt.value()->featureUpdate(); + undeliveredMsgs.erase(msgIt); + } + receipts.erase(it); + } +} + +void ChatForm::clearReciepts() +{ + receipts.clear(); + undeliveredMsgs.clear(); +} + +void ChatForm::deliverOfflineMsgs() +{ + if (!Settings::getInstance().getFauxOfflineMessaging()) + return; + + QMap msgs = undeliveredMsgs; + clearReciepts(); + + for (auto iter = msgs.begin(); iter != msgs.end(); iter++) + { + QString messageText = iter.value()->getRawMessage(); + int rec; + if (iter.value()->isAction()) + rec = Core::getInstance()->sendAction(f->getFriendID(), messageText); + else + rec = Core::getInstance()->sendMessage(f->getFriendID(), messageText); + registerReceipt(rec, iter.key(), iter.value()); + } +} diff --git a/src/widget/form/chatform.h b/src/widget/form/chatform.h index 14b6739e1..2a051f84e 100644 --- a/src/widget/form/chatform.h +++ b/src/widget/form/chatform.h @@ -22,6 +22,7 @@ #include #include #include +#include struct Friend; class FileTransferInstance; @@ -36,6 +37,8 @@ public: ~ChatForm(); void setStatusMessage(QString newMessage); + void dischargeReceipt(int receipt); + signals: void sendFile(int32_t friendId, QString, QString, long long); void startCall(int friendId); @@ -47,6 +50,8 @@ signals: void volMuteToggle(int callId); public slots: + void deliverOfflineMsgs(); + void clearReciepts(); void startFileSend(ToxFile file); void onFileRecvRequest(ToxFile file); void onAvInvite(int FriendId, int CallId, bool video); @@ -80,10 +85,11 @@ private slots: void updateTime(); protected: - void loadHistory(QDateTime since); + void loadHistory(QDateTime since, bool processUndelivered = false); // drag & drop void dragEnterEvent(QDragEnterEvent* ev); void dropEvent(QDropEvent* ev); + void registerReceipt(int receipt, int messageID, MessageActionPtr msg); private: Friend* f; @@ -100,6 +106,8 @@ private: void startCounter(); void stopCounter(); QString secondsToDHMS(quint32 duration); + QHash receipts; + QMap undeliveredMsgs; }; #endif // CHATFORM_H diff --git a/src/widget/form/genericchatform.cpp b/src/widget/form/genericchatform.cpp index 187f61367..ffe6f8ecb 100644 --- a/src/widget/form/genericchatform.cpp +++ b/src/widget/form/genericchatform.cpp @@ -149,6 +149,8 @@ GenericChatForm::GenericChatForm(QWidget *parent) : chatWidget->document()->setDefaultStyleSheet(Style::getStylesheet(":ui/chatArea/innerStyle.css")); chatWidget->setStyleSheet(Style::getStylesheet(":/ui/chatArea/chatArea.css")); headWidget->setStyleSheet(Style::getStylesheet(":/ui/chatArea/chatHead.css")); + + ChatAction::setupFormat(); } bool GenericChatForm::isEmpty() @@ -199,20 +201,28 @@ void GenericChatForm::onSaveLogClicked() */ void GenericChatForm::addMessage(const QString& author, const QString &message, bool isAction, const QDateTime &datetime) { - ChatActionPtr ca = genMessageActionAction(author, message, isAction, datetime); + MessageActionPtr ca = genMessageActionAction(author, message, isAction, datetime); + ca->markAsSent(); chatWidget->insertMessage(ca); } -void GenericChatForm::addMessage(const ToxID& author, const QString &message, bool isAction, const QDateTime &datetime) +MessageActionPtr GenericChatForm::addMessage(const ToxID& author, const QString &message, bool isAction, + const QDateTime &datetime, bool isSent) { - ChatActionPtr ca = genMessageActionAction(author, message, isAction, datetime); + MessageActionPtr ca = genMessageActionAction(author, message, isAction, datetime); + if (isSent) + ca->markAsSent(); chatWidget->insertMessage(ca); + return ca; } -void GenericChatForm::addSelfMessage(const QString &message, bool isAction, const QDateTime &datetime) +MessageActionPtr GenericChatForm::addSelfMessage(const QString &message, bool isAction, const QDateTime &datetime, bool isSent) { - ChatActionPtr ca = genSelfActionAction(message, isAction, datetime); + MessageActionPtr ca = genSelfActionAction(message, isAction, datetime); + if (isSent) + ca->markAsSent(); chatWidget->insertMessage(ca); + return ca; } /** @@ -221,7 +231,9 @@ void GenericChatForm::addSelfMessage(const QString &message, bool isAction, cons void GenericChatForm::addAlertMessage(const QString& author, QString message, QDateTime datetime) { QString date = datetime.toString(Settings::getInstance().getTimestampFormat()); - chatWidget->insertMessage(ChatActionPtr(new AlertAction(author, message, date))); + AlertAction *alact = new AlertAction(author, message, date); + alact->markAsSent(); + chatWidget->insertMessage(ChatActionPtr(alact)); previousId.publicKey = author; } @@ -293,12 +305,15 @@ void GenericChatForm::clearChatArea(bool notinform) delete earliestMessage; earliestMessage = nullptr; } + + emit chatAreaCleared(); } /** * @deprecated The only reason it's still alive is because the groupchat API is a bit limited */ -ChatActionPtr GenericChatForm::genMessageActionAction(const QString &author, QString message, bool isAction, const QDateTime &datetime) +MessageActionPtr GenericChatForm::genMessageActionAction(const QString &author, QString message, bool isAction, + const QDateTime &datetime) { if (earliestMessage == nullptr) { @@ -317,20 +332,20 @@ ChatActionPtr GenericChatForm::genMessageActionAction(const QString &author, QSt if (isAction) { previousId = ToxID(); // next msg has a name regardless - return ChatActionPtr(new ActionAction (getElidedName(author), message, date, isMe)); + return MessageActionPtr(new ActionAction (getElidedName(author), message, date, isMe)); } - ChatActionPtr res; + MessageActionPtr res; if (previousId.publicKey == author) - res = ChatActionPtr(new MessageAction(QString(), message, date, isMe)); + res = MessageActionPtr(new MessageAction(QString(), message, date, isMe)); else - res = ChatActionPtr(new MessageAction(getElidedName(author), message, date, isMe)); + res = MessageActionPtr(new MessageAction(getElidedName(author), message, date, isMe)); previousId.publicKey = author; return res; } -ChatActionPtr GenericChatForm::genMessageActionAction(const ToxID& author, QString message, bool isAction, const QDateTime &datetime) +MessageActionPtr GenericChatForm::genMessageActionAction(const ToxID& author, QString message, bool isAction, const QDateTime &datetime) { if (earliestMessage == nullptr) { @@ -364,20 +379,20 @@ ChatActionPtr GenericChatForm::genMessageActionAction(const ToxID& author, QStri if (isAction) { previousId = ToxID(); // next msg has a name regardless - return ChatActionPtr(new ActionAction (getElidedName(authorStr), message, date, isMe)); + return MessageActionPtr(new ActionAction (getElidedName(authorStr), message, date, isMe)); } - ChatActionPtr res; + MessageActionPtr res; if (previousId == author) - res = ChatActionPtr(new MessageAction(QString(), message, date, isMe)); + res = MessageActionPtr(new MessageAction(QString(), message, date, isMe)); else - res = ChatActionPtr(new MessageAction(getElidedName(authorStr), message, date, isMe)); + res = MessageActionPtr(new MessageAction(getElidedName(authorStr), message, date, isMe)); previousId = author; return res; } -ChatActionPtr GenericChatForm::genSelfActionAction(QString message, bool isAction, const QDateTime &datetime) +MessageActionPtr GenericChatForm::genSelfActionAction(QString message, bool isAction, const QDateTime &datetime) { if (earliestMessage == nullptr) { @@ -398,14 +413,14 @@ ChatActionPtr GenericChatForm::genSelfActionAction(QString message, bool isActio if (isAction) { previousId = ToxID(); // next msg has a name regardless - return ChatActionPtr(new ActionAction (getElidedName(author), message, date, true)); + return MessageActionPtr(new ActionAction (getElidedName(author), message, date, true)); } - ChatActionPtr res; + MessageActionPtr res; if (previousId.isMine()) - res = ChatActionPtr(new MessageAction(QString(), message, date, true)); + res = MessageActionPtr(new MessageAction(QString(), message, date, true)); else - res = ChatActionPtr(new MessageAction(getElidedName(author), message, date, true)); + res = MessageActionPtr(new MessageAction(getElidedName(author), message, date, true)); previousId = Core::getInstance()->getSelfId(); return res; diff --git a/src/widget/form/genericchatform.h b/src/widget/form/genericchatform.h index 660b23408..ddd451ca7 100644 --- a/src/widget/form/genericchatform.h +++ b/src/widget/form/genericchatform.h @@ -21,7 +21,7 @@ #include #include #include -#include "src/widget/tool/chatactions/chataction.h" +#include "src/widget/tool/chatactions/messageaction.h" #include "src/corestructs.h" // Spacing in px inserted when the author of the last message changes @@ -48,9 +48,10 @@ public: virtual void setName(const QString &newName); virtual void show(Ui::MainWindow &ui); + void addMessage(const QString& author, const QString &message, bool isAction, const QDateTime &datetime); ///< Deprecated - void addMessage(const ToxID& author, const QString &message, bool isAction, const QDateTime &datetime); - void addSelfMessage(const QString &message, bool isAction, const QDateTime &datetime); + MessageActionPtr addMessage(const ToxID& author, const QString &message, bool isAction, const QDateTime &datetime, bool isSent); + MessageActionPtr addSelfMessage(const QString &message, bool isAction, const QDateTime &datetime, bool isSent); void addSystemInfoMessage(const QString &message, const QString &type, const QDateTime &datetime); void addAlertMessage(const QString& author, QString message, QDateTime datetime); ///< Deprecated void addAlertMessage(const ToxID& author, QString message, QDateTime datetime); @@ -59,6 +60,7 @@ public: signals: void sendMessage(int, QString); void sendAction(int, QString); + void chatAreaCleared(); public slots: void focusInput(); @@ -72,9 +74,9 @@ protected slots: protected: QString getElidedName(const QString& name); - ChatActionPtr genMessageActionAction(const QString& author, QString message, bool isAction, const QDateTime &datetime); ///< Deprecated - ChatActionPtr genMessageActionAction(const ToxID& author, QString message, bool isAction, const QDateTime &datetime); - ChatActionPtr genSelfActionAction(QString message, bool isAction, const QDateTime &datetime); + MessageActionPtr genMessageActionAction(const QString& author, QString message, bool isAction, const QDateTime &datetime); ///< Deprecated + MessageActionPtr genMessageActionAction(const ToxID& author, QString message, bool isAction, const QDateTime &datetime); + MessageActionPtr genSelfActionAction(QString message, bool isAction, const QDateTime &datetime); ChatActionPtr genSystemInfoAction(const QString &message, const QString &type, const QDateTime &datetime); ToxID previousId; diff --git a/src/widget/form/settings/generalform.cpp b/src/widget/form/settings/generalform.cpp index 3785f8afc..496dccc9f 100644 --- a/src/widget/form/settings/generalform.cpp +++ b/src/widget/form/settings/generalform.cpp @@ -59,6 +59,7 @@ GeneralForm::GeneralForm(SettingsWidget *myParent) : bodyUI->autoacceptFiles->setChecked(Settings::getInstance().getAutoSaveEnabled()); bodyUI->autoSaveFilesDir->setText(Settings::getInstance().getGlobalAutoAcceptDir()); bodyUI->showInFront->setChecked(Settings::getInstance().getShowInFront()); + bodyUI->cbFauxOfflineMessaging->setChecked(Settings::getInstance().getFauxOfflineMessaging()); for (auto entry : SmileyPack::listSmileyPacks()) { @@ -125,6 +126,7 @@ GeneralForm::GeneralForm(SettingsWidget *myParent) : connect(bodyUI->proxyAddr, &QLineEdit::editingFinished, this, &GeneralForm::onProxyAddrEdited); connect(bodyUI->proxyPort, SIGNAL(valueChanged(int)), this, SLOT(onProxyPortEdited(int))); connect(bodyUI->reconnectButton, &QPushButton::clicked, this, &GeneralForm::onReconnectClicked); + connect(bodyUI->cbFauxOfflineMessaging, &QCheckBox::stateChanged, this, &GeneralForm::onFauxOfflineMessaging); } GeneralForm::~GeneralForm() @@ -300,3 +302,7 @@ void GeneralForm::onSetShowInFront() Settings::getInstance().setShowInFront(bodyUI->showInFront->isChecked()); } +void GeneralForm::onFauxOfflineMessaging() +{ + Settings::getInstance().setFauxOfflineMessaging(bodyUI->cbFauxOfflineMessaging->isChecked()); +} diff --git a/src/widget/form/settings/generalform.h b/src/widget/form/settings/generalform.h index 29e818bde..493cdd499 100644 --- a/src/widget/form/settings/generalform.h +++ b/src/widget/form/settings/generalform.h @@ -53,6 +53,7 @@ private slots: void onAutoSaveDirChange(); void onCheckUpdateChanged(); void onSetShowInFront(); + void onFauxOfflineMessaging(); private: Ui::GeneralSettings *bodyUI; diff --git a/src/widget/form/settings/generalsettings.ui b/src/widget/form/settings/generalsettings.ui index c41631c5b..19a18c8cd 100644 --- a/src/widget/form/settings/generalsettings.ui +++ b/src/widget/form/settings/generalsettings.ui @@ -40,7 +40,7 @@ 0 0 511 - 796 + 698 @@ -97,41 +97,41 @@ - - Start in tray - 0 0 + + Start in tray + - - Close to tray - 0 0 + + Close to tray + - - Minimize to tray - 0 0 + + Minimize to tray + @@ -157,6 +157,13 @@ + + + + Faux offline messaging + + + diff --git a/src/widget/form/settings/privacyform.cpp b/src/widget/form/settings/privacyform.cpp index 80748a084..8345f0299 100644 --- a/src/widget/form/settings/privacyform.cpp +++ b/src/widget/form/settings/privacyform.cpp @@ -52,6 +52,7 @@ void PrivacyForm::onEnableLoggingUpdated() Settings::getInstance().setEnableLogging(bodyUI->cbKeepHistory->isChecked()); bodyUI->cbEncryptHistory->setEnabled(bodyUI->cbKeepHistory->isChecked()); HistoryKeeper::getInstance()->resetInstance(); + Widget::getInstance()->clearAllReceipts(); } void PrivacyForm::onTypingNotificationEnabledUpdated() diff --git a/src/widget/tool/chatactions/actionaction.cpp b/src/widget/tool/chatactions/actionaction.cpp index a9f749537..1063f070b 100644 --- a/src/widget/tool/chatactions/actionaction.cpp +++ b/src/widget/tool/chatactions/actionaction.cpp @@ -20,6 +20,7 @@ ActionAction::ActionAction(const QString &author, QString message, const QString &date, const bool& me) : MessageAction(author, author+" "+message, date, me) { + rawMessage = message; } QString ActionAction::getName() @@ -31,3 +32,8 @@ QString ActionAction::getMessage() { return MessageAction::getMessage("action"); } + +QString ActionAction::getRawMessage() +{ + return rawMessage; +} diff --git a/src/widget/tool/chatactions/actionaction.h b/src/widget/tool/chatactions/actionaction.h index 484af272b..62b101ef0 100644 --- a/src/widget/tool/chatactions/actionaction.h +++ b/src/widget/tool/chatactions/actionaction.h @@ -24,12 +24,15 @@ class ActionAction : public MessageAction public: ActionAction(const QString &author, QString message, const QString& date, const bool&); virtual ~ActionAction(){;} + virtual QString getRawMessage(); + virtual bool isAction() {return true;} + +protected: virtual QString getMessage(); virtual QString getName(); - virtual void setup(QTextCursor, QTextEdit*) override {;} private: - QString message; + QString message, rawMessage; }; #endif // MESSAGEACTION_H diff --git a/src/widget/tool/chatactions/alertaction.cpp b/src/widget/tool/chatactions/alertaction.cpp index b62c1896e..23179340d 100644 --- a/src/widget/tool/chatactions/alertaction.cpp +++ b/src/widget/tool/chatactions/alertaction.cpp @@ -21,26 +21,6 @@ AlertAction::AlertAction(const QString &author, const QString &message, const QS { } -void AlertAction::setup(QTextCursor cursor, QTextEdit *) -{ - // When this function is called, we're supposed to only update ourselve when needed - // Nobody should ask us to do anything with our content, we're on our own - // Except we never udpate on our own, so we can safely free our resources - - (void) cursor; - message.clear(); - message.squeeze(); - name.clear(); - name.squeeze(); - date.clear(); - date.squeeze(); -} -/* -QString AlertAction::getName() -{ - return QString("
%2
").arg("alert_name").arg(toHtmlChars(name)); -} -*/ QString AlertAction::getMessage() { return MessageAction::getMessage("alert"); diff --git a/src/widget/tool/chatactions/alertaction.h b/src/widget/tool/chatactions/alertaction.h index 64e00e6bf..d37f355bd 100644 --- a/src/widget/tool/chatactions/alertaction.h +++ b/src/widget/tool/chatactions/alertaction.h @@ -24,9 +24,9 @@ class AlertAction : public MessageAction public: AlertAction(const QString &author, const QString &message, const QString& date); virtual ~AlertAction(){;} + +protected: virtual QString getMessage(); - //virtual QString getName(); only do the message for now; preferably would do the whole row - virtual void setup(QTextCursor cursor, QTextEdit*) override; private: QString message; diff --git a/src/widget/tool/chatactions/chataction.cpp b/src/widget/tool/chatactions/chataction.cpp index 6978711a6..a2aa3e140 100644 --- a/src/widget/tool/chatactions/chataction.cpp +++ b/src/widget/tool/chatactions/chataction.cpp @@ -17,6 +17,11 @@ #include "chataction.h" #include #include +#include +#include +#include + +QTextBlockFormat ChatAction::nameFormat, ChatAction::dateFormat; QString ChatAction::toHtmlChars(const QString &str) { @@ -53,3 +58,59 @@ QString ChatAction::getDate() else return QString("
" + toHtmlChars(date) + "
"); } + +void ChatAction::assignPlace(QTextTable *position, QTextEdit *te) +{ + textTable = position; + cur = position->cellAt(0, 2).firstCursorPosition(); + cur.clearSelection(); + cur.setKeepPositionOnInsert(true); + textEdit = te; +} + +void ChatAction::dispaly() +{ + textTable->cellAt(0, 0).firstCursorPosition().setBlockFormat(nameFormat); + textTable->cellAt(0, 0).firstCursorPosition().insertHtml(getName()); + textTable->cellAt(0, 2).firstCursorPosition().insertHtml(getMessage()); + textTable->cellAt(0, 4).firstCursorPosition().setBlockFormat(dateFormat); + textTable->cellAt(0, 4).firstCursorPosition().insertHtml(getDate()); + + cur.setKeepPositionOnInsert(true); + int end=cur.selectionEnd(); + cur.setPosition(cur.position()); + cur.setPosition(end, QTextCursor::KeepAnchor); + + featureUpdate(); +} + +void ChatAction::setupFormat() +{ + nameFormat.setAlignment(Qt::AlignRight); + nameFormat.setNonBreakableLines(true); + dateFormat.setAlignment(Qt::AlignLeft); + dateFormat.setNonBreakableLines(true); +} + +void ChatAction::updateContent() +{ + if (cur.isNull() || !textEdit) + return; + + int vSliderVal = textEdit->verticalScrollBar()->value(); + + // update content + int pos = cur.selectionStart(); + cur.removeSelectedText(); + cur.setKeepPositionOnInsert(false); + cur.insertHtml(getMessage()); + cur.setKeepPositionOnInsert(true); + int end = cur.position(); + cur.setPosition(pos); + cur.setPosition(end, QTextCursor::KeepAnchor); + + // restore old slider value + textEdit->verticalScrollBar()->setValue(vSliderVal); + + featureUpdate(); +} diff --git a/src/widget/tool/chatactions/chataction.h b/src/widget/tool/chatactions/chataction.h index d7da9fa92..b007ecb60 100644 --- a/src/widget/tool/chatactions/chataction.h +++ b/src/widget/tool/chatactions/chataction.h @@ -23,26 +23,42 @@ class FileTransferInstance; class QTextEdit; +class QTextTable; class ChatAction : public QObject { + Q_OBJECT public: ChatAction(const bool &me, const QString &author, const QString &date) : isMe(me), name(author), date(date) {;} virtual ~ChatAction(){;} - virtual void setup(QTextCursor cursor, QTextEdit* textEdit) = 0; ///< Call once, and then you MUST let the object update itself + void assignPlace(QTextTable *position, QTextEdit* te); + virtual void dispaly(); + virtual bool isInteractive(){return false;} + virtual void featureUpdate() {;} + + static void setupFormat(); + +public slots: + void updateContent(); + +protected: virtual QString getName(); virtual QString getMessage() = 0; virtual QString getDate(); - virtual bool isInteractive(){return false;} -protected: QString toHtmlChars(const QString &str); QString QImage2base64(const QImage &img); protected: bool isMe; QString name, date; + + QTextTable *textTable; + QTextEdit *textEdit; + QTextCursor cur; + + static QTextBlockFormat nameFormat, dateFormat; }; typedef QSharedPointer ChatActionPtr; diff --git a/src/widget/tool/chatactions/filetransferaction.cpp b/src/widget/tool/chatactions/filetransferaction.cpp index 01910813b..103e72f36 100644 --- a/src/widget/tool/chatactions/filetransferaction.cpp +++ b/src/widget/tool/chatactions/filetransferaction.cpp @@ -22,11 +22,10 @@ FileTransferAction::FileTransferAction(FileTransferInstance *widget, const QString &author, const QString &date, const bool &me) : ChatAction(me, author, date) - , edit(nullptr) { w = widget; - connect(w, &FileTransferInstance::stateUpdated, this, &FileTransferAction::updateHtml); + connect(w, &FileTransferInstance::stateUpdated, this, &FileTransferAction::updateContent); } FileTransferAction::~FileTransferAction() @@ -43,6 +42,7 @@ QString FileTransferAction::getMessage() return widgetHtml; } +/* void FileTransferAction::setup(QTextCursor cursor, QTextEdit *textEdit) { cur = cursor; @@ -53,7 +53,8 @@ void FileTransferAction::setup(QTextCursor cursor, QTextEdit *textEdit) edit = textEdit; } - +*/ +/* void FileTransferAction::updateHtml() { if (cur.isNull() || !edit) @@ -75,7 +76,7 @@ void FileTransferAction::updateHtml() // restore old slider value edit->verticalScrollBar()->setValue(vSliderVal); } - +*/ bool FileTransferAction::isInteractive() { if (w->getState() == FileTransferInstance::TransfState::tsCanceled diff --git a/src/widget/tool/chatactions/filetransferaction.h b/src/widget/tool/chatactions/filetransferaction.h index 816975ee2..696d4a5c0 100644 --- a/src/widget/tool/chatactions/filetransferaction.h +++ b/src/widget/tool/chatactions/filetransferaction.h @@ -25,17 +25,13 @@ class FileTransferAction : public ChatAction public: FileTransferAction(FileTransferInstance *widget, const QString &author, const QString &date, const bool &me); virtual ~FileTransferAction(); - virtual QString getMessage(); - virtual void setup(QTextCursor cursor, QTextEdit* textEdit) override; virtual bool isInteractive(); -private slots: - void updateHtml(); +protected: + virtual QString getMessage(); private: FileTransferInstance *w; - QTextCursor cur; - QTextEdit* edit; }; #endif // FILETRANSFERACTION_H diff --git a/src/widget/tool/chatactions/messageaction.cpp b/src/widget/tool/chatactions/messageaction.cpp index 499299463..42e46bc79 100644 --- a/src/widget/tool/chatactions/messageaction.cpp +++ b/src/widget/tool/chatactions/messageaction.cpp @@ -17,11 +17,13 @@ #include "messageaction.h" #include "src/misc/smileypack.h" #include "src/misc/settings.h" +#include MessageAction::MessageAction(const QString &author, const QString &message, const QString &date, const bool &me) : ChatAction(me, author, date), message(message) { + isProcessed = false; } QString MessageAction::getMessage(QString div) @@ -71,3 +73,24 @@ QString MessageAction::getMessage() else return getMessage("message"); } + +void MessageAction::featureUpdate() +{ + QTextTableCell cell = textTable->cellAt(0,3); + QTextTableCellFormat format; + if (!isProcessed) + format.setBackground(QColor(Qt::red)); + else + format.setBackground(QColor(Qt::white)); + cell.setFormat(format); +} + +void MessageAction::markAsSent() +{ + isProcessed = true; +} + +QString MessageAction::getRawMessage() +{ + return message; +} diff --git a/src/widget/tool/chatactions/messageaction.h b/src/widget/tool/chatactions/messageaction.h index d0e8f4066..9c11bbda4 100644 --- a/src/widget/tool/chatactions/messageaction.h +++ b/src/widget/tool/chatactions/messageaction.h @@ -24,12 +24,20 @@ class MessageAction : public ChatAction public: MessageAction(const QString &author, const QString &message, const QString &date, const bool &me); virtual ~MessageAction(){;} + virtual void featureUpdate(); + void markAsSent(); + virtual QString getRawMessage(); + virtual bool isAction() {return false;} + +protected: virtual QString getMessage(); virtual QString getMessage(QString div); - virtual void setup(QTextCursor, QTextEdit*) override {;} protected: QString message; + bool isProcessed; }; +typedef QSharedPointer MessageActionPtr; + #endif // MESSAGEACTION_H diff --git a/src/widget/tool/chatactions/systemmessageaction.h b/src/widget/tool/chatactions/systemmessageaction.h index 1358f7c88..99740cedc 100644 --- a/src/widget/tool/chatactions/systemmessageaction.h +++ b/src/widget/tool/chatactions/systemmessageaction.h @@ -24,8 +24,8 @@ class SystemMessageAction : public ChatAction public: SystemMessageAction(const QString &message, const QString& type, const QString &date); virtual ~SystemMessageAction(){;} - virtual void setup(QTextCursor, QTextEdit*) override {;} +protected: virtual QString getName() {return QString();} virtual QString getMessage(); diff --git a/src/widget/widget.cpp b/src/widget/widget.cpp index 3f2792de6..cb60a1491 100644 --- a/src/widget/widget.cpp +++ b/src/widget/widget.cpp @@ -213,6 +213,7 @@ void Widget::init() connect(core, &Core::friendStatusMessageChanged, this, &Widget::onFriendStatusMessageChanged); connect(core, &Core::friendRequestReceived, this, &Widget::onFriendRequestReceived); connect(core, &Core::friendMessageReceived, this, &Widget::onFriendMessageReceived); + connect(core, &Core::receiptRecieved, this, &Widget::onReceiptRecieved); connect(core, &Core::groupInviteReceived, this, &Widget::onGroupInviteReceived); connect(core, &Core::groupMessageReceived, this, &Widget::onGroupMessageReceived); connect(core, &Core::groupNamelistChanged, this, &Widget::onGroupNamelistChanged); @@ -691,6 +692,11 @@ void Widget::onFriendStatusChanged(int friendId, Status status) f->getChatForm()->addSystemInfoMessage(tr("%1 is now %2", "e.g. \"Dubslow is now online\"").arg(f->getDisplayedName()).arg(fStatus), "white", QDateTime::currentDateTime()); } + + if (isActualChange && status != Status::Offline) + { // wait a little + QTimer::singleShot(250, f->getChatForm(), SLOT(deliverOfflineMsgs())); + } } void Widget::onFriendStatusMessageChanged(int friendId, const QString& message) @@ -737,12 +743,12 @@ void Widget::onFriendMessageReceived(int friendId, const QString& message, bool return; QDateTime timestamp = QDateTime::currentDateTime(); - f->getChatForm()->addMessage(f->getToxID(), message, isAction, timestamp); + f->getChatForm()->addMessage(f->getToxID(), message, isAction, timestamp, true); if (isAction) - HistoryKeeper::getInstance()->addChatEntry(f->getToxID().publicKey, "/me " + message, f->getToxID().publicKey, timestamp); + HistoryKeeper::getInstance()->addChatEntry(f->getToxID().publicKey, "/me " + message, f->getToxID().publicKey, timestamp, true); else - HistoryKeeper::getInstance()->addChatEntry(f->getToxID().publicKey, message, f->getToxID().publicKey, timestamp); + HistoryKeeper::getInstance()->addChatEntry(f->getToxID().publicKey, message, f->getToxID().publicKey, timestamp, true); if (activeChatroomWidget != nullptr) { @@ -761,6 +767,15 @@ void Widget::onFriendMessageReceived(int friendId, const QString& message, bool f->getFriendWidget()->updateStatusLight(); } +void Widget::onReceiptRecieved(int friendId, int receipt) +{ + Friend* f = FriendList::findFriend(friendId); + if (!f) + return; + + f->getChatForm()->dischargeReceipt(receipt); +} + void Widget::newMessageAlert(GenericChatroomWidget* chat) { QApplication::alert(this); @@ -1047,12 +1062,10 @@ void Widget::setStatusBusy() void Widget::onMessageSendResult(int friendId, const QString& message, int messageId) { Q_UNUSED(message) + Q_UNUSED(messageId) Friend* f = FriendList::findFriend(friendId); if (!f) return; - - if (!messageId) - f->getChatForm()->addSystemInfoMessage(tr("Message failed to send"), "red", QDateTime::currentDateTime()); } void Widget::onGroupSendResult(int groupId, const QString& message, int result) @@ -1129,3 +1142,12 @@ bool Widget::askMsgboxQuestion(const QString& title, const QString& msg) return QMessageBox::question(this, title, msg) == QMessageBox::StandardButton::Yes; } } + +void Widget::clearAllReceipts() +{ + QList frnds = FriendList::getAllFriends(); + for (Friend *f : frnds) + { + f->getChatForm()->clearReciepts(); + } +} diff --git a/src/widget/widget.h b/src/widget/widget.h index 22e7f4667..9a45277da 100644 --- a/src/widget/widget.h +++ b/src/widget/widget.h @@ -73,6 +73,7 @@ public: virtual void closeEvent(QCloseEvent *event); virtual void changeEvent(QEvent *event); + void clearAllReceipts(); public slots: void onSettingsClicked(); @@ -109,6 +110,7 @@ private slots: void onChatroomWidgetClicked(GenericChatroomWidget *); void onFriendMessageReceived(int friendId, const QString& message, bool isAction); void onFriendRequestReceived(const QString& userId, const QString& message); + void onReceiptRecieved(int friendId, int receipt); void onEmptyGroupCreated(int groupId); void onGroupInviteReceived(int32_t friendId, const uint8_t *publicKey,uint16_t length); void onGroupMessageReceived(int groupnumber, const QString& message, const QString& author, bool isAction);