now instead of storing uploading message in ram I store them in database to be able to recover unsent ones on the next statrt. Found and fixed bug with spam repaints in feedview because of icons

This commit is contained in:
Blue 2021-05-07 21:26:02 +03:00
parent ebf0c64ffb
commit f45319de25
9 changed files with 146 additions and 59 deletions

View File

@ -271,6 +271,8 @@ void Core::Archive::changeMessage(const QString& id, const QMap<QString, QVarian
bool hadStanzaId = msg.getStanzaId().size() > 0; bool hadStanzaId = msg.getStanzaId().size() > 0;
QDateTime oTime = msg.getTime(); QDateTime oTime = msg.getTime();
bool idChange = msg.change(data); bool idChange = msg.change(data);
QDateTime nTime = msg.getTime();
bool orderChange = oTime != nTime;
MDB_val lmdbKey, lmdbData; MDB_val lmdbKey, lmdbData;
QByteArray ba; QByteArray ba;
@ -280,15 +282,21 @@ void Core::Archive::changeMessage(const QString& id, const QMap<QString, QVarian
lmdbKey.mv_size = strId.size(); lmdbKey.mv_size = strId.size();
lmdbKey.mv_data = (char*)strId.c_str(); lmdbKey.mv_data = (char*)strId.c_str();
int rc; int rc;
if (idChange || orderChange) {
if (idChange) { if (idChange) {
rc = mdb_del(txn, main, &lmdbKey, &lmdbData); rc = mdb_del(txn, main, &lmdbKey, &lmdbData);
} else {
quint64 ostamp = oTime.toMSecsSinceEpoch();
lmdbData.mv_data = (quint8*)&ostamp;
lmdbData.mv_size = 8;
rc = mdb_del(txn, order, &lmdbData, &lmdbKey);
}
if (rc == 0) { if (rc == 0) {
strId = msg.getId().toStdString(); strId = msg.getId().toStdString();
lmdbKey.mv_size = strId.size(); lmdbKey.mv_size = strId.size();
lmdbKey.mv_data = (char*)strId.c_str(); lmdbKey.mv_data = (char*)strId.c_str();
quint64 stamp = nTime.toMSecsSinceEpoch();
quint64 stamp = oTime.toMSecsSinceEpoch();
lmdbData.mv_data = (quint8*)&stamp; lmdbData.mv_data = (quint8*)&stamp;
lmdbData.mv_size = 8; lmdbData.mv_size = 8;
rc = mdb_put(txn, order, &lmdbData, &lmdbKey, 0); rc = mdb_put(txn, order, &lmdbData, &lmdbKey, 0);

View File

@ -23,7 +23,6 @@ Core::MessageHandler::MessageHandler(Core::Account* account):
QObject(), QObject(),
acc(account), acc(account),
pendingStateMessages(), pendingStateMessages(),
pendingMessages(),
uploadingSlotsQueue() uploadingSlotsQueue()
{ {
} }
@ -249,12 +248,13 @@ void Core::MessageHandler::sendMessage(const Shared::Message& data)
} }
} }
void Core::MessageHandler::performSending(Shared::Message data) void Core::MessageHandler::performSending(Shared::Message data, bool newMessage)
{ {
QString jid = data.getPenPalJid(); QString jid = data.getPenPalJid();
QString id = data.getId(); QString id = data.getId();
QString oob = data.getOutOfBandUrl(); QString oob = data.getOutOfBandUrl();
RosterItem* ri = acc->rh->getRosterItem(jid); RosterItem* ri = acc->rh->getRosterItem(jid);
bool sent = false;
QMap<QString, QVariant> changes; QMap<QString, QVariant> changes;
if (acc->state == Shared::ConnectionState::connected) { if (acc->state == Shared::ConnectionState::connected) {
QXmppMessage msg(acc->getFullJid(), data.getTo(), data.getBody(), data.getThread()); QXmppMessage msg(acc->getFullJid(), data.getTo(), data.getBody(), data.getThread());
@ -267,20 +267,13 @@ void Core::MessageHandler::performSending(Shared::Message data)
msg.setOutOfBandUrl(oob); msg.setOutOfBandUrl(oob);
msg.setReceiptRequested(true); msg.setReceiptRequested(true);
bool sent = acc->client.sendPacket(msg); sent = acc->client.sendPacket(msg);
if (sent) { if (sent) {
data.setState(Shared::Message::State::sent); data.setState(Shared::Message::State::sent);
} else { } else {
data.setState(Shared::Message::State::error); data.setState(Shared::Message::State::error);
data.setErrorText("Couldn't send message via QXMPP library check out logs"); data.setErrorText("Couldn't send message: internal QXMPP library error, probably need to check out the logs");
}
if (ri != 0) {
ri->appendMessageToArchive(data);
if (sent) {
pendingStateMessages.insert(std::make_pair(id, jid));
}
} }
} else { } else {
@ -296,6 +289,22 @@ void Core::MessageHandler::performSending(Shared::Message data)
if (oob.size() > 0) { if (oob.size() > 0) {
changes.insert("outOfBandUrl", oob); changes.insert("outOfBandUrl", oob);
} }
if (!newMessage) {
changes.insert("stamp", data.getTime());
}
if (ri != 0) {
if (newMessage) {
ri->appendMessageToArchive(data);
} else {
ri->changeMessage(id, changes);
}
if (sent) {
pendingStateMessages.insert(std::make_pair(id, jid));
} else {
pendingStateMessages.erase(id);
}
}
emit acc->changeMessage(jid, id, changes); emit acc->changeMessage(jid, id, changes);
} }
@ -303,27 +312,37 @@ void Core::MessageHandler::performSending(Shared::Message data)
void Core::MessageHandler::prepareUpload(const Shared::Message& data) void Core::MessageHandler::prepareUpload(const Shared::Message& data)
{ {
if (acc->state == Shared::ConnectionState::connected) { if (acc->state == Shared::ConnectionState::connected) {
QString jid = data.getPenPalJid();
QString id = data.getId();
RosterItem* ri = acc->rh->getRosterItem(jid);
if (!ri) {
qDebug() << "An attempt to initialize upload in" << acc->name << "for pal" << jid << "but the object for this pal wasn't found, something went terrebly wrong, skipping send";
return;
}
QString path = data.getAttachPath(); QString path = data.getAttachPath();
QString url = acc->network->getFileRemoteUrl(path); QString url = acc->network->getFileRemoteUrl(path);
if (url.size() != 0) { if (url.size() != 0) {
sendMessageWithLocalUploadedFile(data, url); sendMessageWithLocalUploadedFile(data, url);
} else { } else {
if (acc->network->checkAndAddToUploading(acc->getName(), data.getPenPalJid(), data.getId(), path)) { if (acc->network->checkAndAddToUploading(acc->getName(), jid, id, path)) {
pendingMessages.emplace(data.getId(), data); ri->appendMessageToArchive(data);
pendingStateMessages.insert(std::make_pair(id, jid));
} else { } else {
if (acc->um->serviceFound()) { if (acc->um->serviceFound()) {
QFileInfo file(path); QFileInfo file(path);
if (file.exists() && file.isReadable()) { if (file.exists() && file.isReadable()) {
uploadingSlotsQueue.emplace_back(path, data); ri->appendMessageToArchive(data);
pendingStateMessages.insert(std::make_pair(id, jid));
uploadingSlotsQueue.emplace_back(path, id);
if (uploadingSlotsQueue.size() == 1) { if (uploadingSlotsQueue.size() == 1) {
acc->um->requestUploadSlot(file); acc->um->requestUploadSlot(file);
} }
} else { } else {
handleUploadError(data.getPenPalJid(), data.getId(), "Uploading file no longer exists or your system user has no permission to read it"); handleUploadError(jid, id, "Uploading file no longer exists or your system user has no permission to read it");
qDebug() << "Requested upload slot in account" << acc->name << "for file" << path << "but the file doesn't exist or is not readable"; qDebug() << "Requested upload slot in account" << acc->name << "for file" << path << "but the file doesn't exist or is not readable";
} }
} else { } else {
handleUploadError(data.getPenPalJid(), data.getId(), "Your server doesn't support file upload service, or it's prohibited for your account"); handleUploadError(jid, id, "Your server doesn't support file upload service, or it's prohibited for your account");
qDebug() << "Requested upload slot in account" << acc->name << "for file" << path << "but upload manager didn't discover any upload services"; qDebug() << "Requested upload slot in account" << acc->name << "for file" << path << "but upload manager didn't discover any upload services";
} }
} }
@ -340,10 +359,10 @@ void Core::MessageHandler::onUploadSlotReceived(const QXmppHttpUploadSlotIq& slo
if (uploadingSlotsQueue.size() == 0) { if (uploadingSlotsQueue.size() == 0) {
qDebug() << "HTTP Upload manager of account" << acc->name << "reports about success requesting upload slot, but none was requested"; qDebug() << "HTTP Upload manager of account" << acc->name << "reports about success requesting upload slot, but none was requested";
} else { } else {
const std::pair<QString, Shared::Message>& pair = uploadingSlotsQueue.front(); const std::pair<QString, QString>& pair = uploadingSlotsQueue.front();
const QString& mId = pair.second.getId(); const QString& mId = pair.second;
acc->network->uploadFile({acc->name, pair.second.getPenPalJid(), mId}, pair.first, slot.putUrl(), slot.getUrl(), slot.putHeaders()); QString palJid = pendingStateMessages.at(mId);
pendingMessages.emplace(mId, pair.second); acc->network->uploadFile({acc->name, palJid, mId}, pair.first, slot.putUrl(), slot.getUrl(), slot.putHeaders());
uploadingSlotsQueue.pop_front(); uploadingSlotsQueue.pop_front();
if (uploadingSlotsQueue.size() > 0) { if (uploadingSlotsQueue.size() > 0) {
@ -359,9 +378,9 @@ void Core::MessageHandler::onUploadSlotRequestFailed(const QXmppHttpUploadReques
qDebug() << "HTTP Upload manager of account" << acc->name << "reports about an error requesting upload slot, but none was requested"; qDebug() << "HTTP Upload manager of account" << acc->name << "reports about an error requesting upload slot, but none was requested";
qDebug() << err; qDebug() << err;
} else { } else {
const std::pair<QString, Shared::Message>& pair = uploadingSlotsQueue.front(); const std::pair<QString, QString>& pair = uploadingSlotsQueue.front();
qDebug() << "Error requesting upload slot for file" << pair.first << "in account" << acc->name << ":" << err; qDebug() << "Error requesting upload slot for file" << pair.first << "in account" << acc->name << ":" << err;
emit acc->uploadFileError(pair.second.getPenPalJid(), pair.second.getId(), "Error requesting slot to upload file: " + err); handleUploadError(pendingStateMessages.at(pair.second), pair.second, err);
uploadingSlotsQueue.pop_front(); uploadingSlotsQueue.pop_front();
if (uploadingSlotsQueue.size() > 0) { if (uploadingSlotsQueue.size() > 0) {
@ -400,47 +419,65 @@ void Core::MessageHandler::onLoadFileError(const std::list<Shared::MessageInfo>&
void Core::MessageHandler::handleUploadError(const QString& jid, const QString& messageId, const QString& errorText) void Core::MessageHandler::handleUploadError(const QString& jid, const QString& messageId, const QString& errorText)
{ {
std::map<QString, Shared::Message>::const_iterator itr = pendingMessages.find(messageId); emit acc->uploadFileError(jid, messageId, "Error requesting slot to upload file: " + errorText);
if (itr != pendingMessages.end()) { pendingStateMessages.erase(jid);
pendingMessages.erase(itr); requestChangeMessage(jid, messageId, {
//TODO move the storage of pending messages to the database and change them there {"state", static_cast<uint>(Shared::Message::State::error)},
} {"errorText", errorText}
});
} }
void Core::MessageHandler::onUploadFileComplete(const std::list<Shared::MessageInfo>& msgs, const QString& path) void Core::MessageHandler::onUploadFileComplete(const std::list<Shared::MessageInfo>& msgs, const QString& path)
{ {
for (const Shared::MessageInfo& info : msgs) { for (const Shared::MessageInfo& info : msgs) {
if (info.account == acc->getName()) { if (info.account == acc->getName()) {
std::map<QString, Shared::Message>::const_iterator itr = pendingMessages.find(info.messageId); RosterItem* ri = acc->rh->getRosterItem(info.jid);
if (itr != pendingMessages.end()) { if (ri != 0) {
sendMessageWithLocalUploadedFile(itr->second, path); Shared::Message msg = ri->getMessage(info.messageId);
pendingMessages.erase(itr); sendMessageWithLocalUploadedFile(msg, path, false);
} else {
qDebug() << "A signal received about complete upload to" << acc->name << "for pal" << info.jid << "but the object for this pal wasn't found, something went terrebly wrong, skipping send";
} }
} }
} }
} }
void Core::MessageHandler::sendMessageWithLocalUploadedFile(Shared::Message msg, const QString& url) void Core::MessageHandler::sendMessageWithLocalUploadedFile(Shared::Message msg, const QString& url, bool newMessage)
{ {
msg.setOutOfBandUrl(url); msg.setOutOfBandUrl(url);
if (msg.getBody().size() == 0) { if (msg.getBody().size() == 0) { //not sure why, but most messages do that
msg.setBody(url); msg.setBody(url); //they duplicate oob in body, some of them wouldn't even show an attachment if you don't do that
} }
performSending(msg); performSending(msg, newMessage);
//TODO removal/progress update //TODO removal/progress update
} }
static const std::set<QString> allowerToChangeKeys({
"attachPath",
"outOfBandUrl",
"state",
"errorText"
});
void Core::MessageHandler::requestChangeMessage(const QString& jid, const QString& messageId, const QMap<QString, QVariant>& data) void Core::MessageHandler::requestChangeMessage(const QString& jid, const QString& messageId, const QMap<QString, QVariant>& data)
{ {
RosterItem* cnt = acc->rh->getRosterItem(jid); RosterItem* cnt = acc->rh->getRosterItem(jid);
if (cnt != 0) { if (cnt != 0) {
QMap<QString, QVariant>::const_iterator itr = data.find("attachPath"); bool allSupported = true;
if (data.size() == 1 && itr != data.end()) { QString unsupportedString;
for (QMap<QString, QVariant>::const_iterator itr = data.begin(); itr != data.end(); ++itr) { //I need all this madness
if (allowerToChangeKeys.count(itr.key()) != 1) { //to not allow this method
allSupported = false; //to make a message to look like if it was edited
unsupportedString = itr.key(); //basically I needed to control who exaclty calls this method
break; //because the underlying tech assumes that the change is initiated by user
} //not by system
}
if (allSupported) {
cnt->changeMessage(messageId, data); cnt->changeMessage(messageId, data);
emit acc->changeMessage(jid, messageId, data); emit acc->changeMessage(jid, messageId, data);
} else { } else {
qDebug() << "A request to change message" << messageId << "of conversation" << jid << "with following data" << data; qDebug() << "A request to change message" << messageId << "of conversation" << jid << "with following data" << data;
qDebug() << "nothing but the changing of the local path is supported yet in this method, skipping"; qDebug() << "only limited set of dataFields are supported yet here, and" << unsupportedString << "isn't one of them, skipping";
} }
} }
} }

View File

@ -64,16 +64,15 @@ private:
bool handleChatMessage(const QXmppMessage& msg, bool outgoing = false, bool forwarded = false, bool guessing = false); bool handleChatMessage(const QXmppMessage& msg, bool outgoing = false, bool forwarded = false, bool guessing = false);
bool handleGroupMessage(const QXmppMessage& msg, bool outgoing = false, bool forwarded = false, bool guessing = false); bool handleGroupMessage(const QXmppMessage& msg, bool outgoing = false, bool forwarded = false, bool guessing = false);
void logMessage(const QXmppMessage& msg, const QString& reason = "Message wasn't handled: "); void logMessage(const QXmppMessage& msg, const QString& reason = "Message wasn't handled: ");
void sendMessageWithLocalUploadedFile(Shared::Message msg, const QString& url); void sendMessageWithLocalUploadedFile(Shared::Message msg, const QString& url, bool newMessage = true);
void performSending(Shared::Message data); void performSending(Shared::Message data, bool newMessage = true);
void prepareUpload(const Shared::Message& data); void prepareUpload(const Shared::Message& data);
void handleUploadError(const QString& jid, const QString& messageId, const QString& errorText); void handleUploadError(const QString& jid, const QString& messageId, const QString& errorText);
private: private:
Account* acc; Account* acc;
std::map<QString, QString> pendingStateMessages; std::map<QString, QString> pendingStateMessages; //key is message id, value is JID
std::map<QString, Shared::Message> pendingMessages; std::deque<std::pair<QString, QString>> uploadingSlotsQueue;
std::deque<std::pair<QString, Shared::Message>> uploadingSlotsQueue;
}; };
} }

View File

@ -569,3 +569,20 @@ void Core::RosterItem::downgradeDatabaseState()
archiveState = ArchiveState::chunk; archiveState = ArchiveState::chunk;
} }
} }
Shared::Message Core::RosterItem::getMessage(const QString& id)
{
for (const Shared::Message& msg : appendCache) {
if (msg.getId() == id) {
return msg;
}
}
for (Shared::Message& msg : hisoryCache) {
if (msg.getId() == id) {
return msg;
}
}
return archive->getElement(id);
}

View File

@ -78,6 +78,8 @@ public:
void clearArchiveRequests(); void clearArchiveRequests();
void downgradeDatabaseState(); void downgradeDatabaseState();
Shared::Message getMessage(const QString& id);
signals: signals:
void nameChanged(const QString& name); void nameChanged(const QString& name);
void subscriptionStateChanged(Shared::SubscriptionState state); void subscriptionStateChanged(Shared::SubscriptionState state);

View File

@ -410,6 +410,14 @@ bool Shared::Message::change(const QMap<QString, QVariant>& data)
setEdited(true); setEdited(true);
} }
} }
} else {
QMap<QString, QVariant>::const_iterator dItr = data.find("stamp");
if (dItr != data.end()) {
QDateTime ntime = dItr.value().toDateTime();
if (time != ntime) {
setTime(ntime);
}
}
} }
return idChanged; return idChanged;
@ -437,7 +445,7 @@ void Shared::Message::setOutOfBandUrl(const QString& url)
bool Shared::Message::storable() const bool Shared::Message::storable() const
{ {
return id.size() > 0 && (body.size() > 0 || oob.size()) > 0; return id.size() > 0 && (body.size() > 0 || oob.size() > 0 || attachPath.size() > 0);
} }
void Shared::Message::setStanzaId(const QString& sid) void Shared::Message::setStanzaId(const QString& sid)

View File

@ -194,6 +194,14 @@ std::set<Models::MessageFeed::MessageRoles> Models::MessageFeed::detectChanges(c
roles.insert(MessageRoles::Text); roles.insert(MessageRoles::Text);
roles.insert(MessageRoles::Correction); roles.insert(MessageRoles::Correction);
} }
} else {
QMap<QString, QVariant>::const_iterator dItr = data.find("stamp");
if (dItr != data.end()) {
QDateTime ntime = dItr.value().toDateTime();
if (msg.getTime() != ntime) {
roles.insert(MessageRoles::Date);
}
}
} }
return roles; return roles;

View File

@ -34,8 +34,8 @@ const std::set<int> FeedView::geometryChangingRoles = {
Models::MessageFeed::Attach, Models::MessageFeed::Attach,
Models::MessageFeed::Text, Models::MessageFeed::Text,
Models::MessageFeed::Id, Models::MessageFeed::Id,
Models::MessageFeed::Error Models::MessageFeed::Error,
Models::MessageFeed::Date
}; };
FeedView::FeedView(QWidget* parent): FeedView::FeedView(QWidget* parent):

View File

@ -397,13 +397,6 @@ QLabel * MessageDelegate::getStatusIcon(const Models::FeedItem& data) const
std::map<QString, QLabel*>::const_iterator itr = statusIcons->find(data.id); std::map<QString, QLabel*>::const_iterator itr = statusIcons->find(data.id);
QLabel* result = 0; QLabel* result = 0;
if (itr != statusIcons->end()) {
result = itr->second;
} else {
result = new QLabel();
statusIcons->insert(std::make_pair(data.id, result));
}
QIcon q(Shared::icon(Shared::messageStateThemeIcons[static_cast<uint8_t>(data.state)])); QIcon q(Shared::icon(Shared::messageStateThemeIcons[static_cast<uint8_t>(data.state)]));
QString tt = Shared::Global::getName(data.state); QString tt = Shared::Global::getName(data.state);
if (data.state == Shared::Message::State::error) { if (data.state == Shared::Message::State::error) {
@ -412,8 +405,23 @@ QLabel * MessageDelegate::getStatusIcon(const Models::FeedItem& data) const
} }
} }
result->setToolTip(tt); if (itr != statusIcons->end()) {
result = itr->second;
if (result->toolTip() != tt) { //If i just assign pixmap every time unconditionally
result->setPixmap(q.pixmap(statusIconSize)); //it involves into an infinite cycle of repaint
result->setToolTip(tt); //may be it's better to subclass and store last condition in int?
}
} else {
result = new QLabel();
statusIcons->insert(std::make_pair(data.id, result));
result->setPixmap(q.pixmap(statusIconSize)); result->setPixmap(q.pixmap(statusIconSize));
result->setToolTip(tt);
}
result->setToolTip(tt);
//result->setText(std::to_string((int)data.state).c_str());
return result; return result;
} }