basic error download/upload files handling, need more testing

This commit is contained in:
Blue 2021-05-14 22:49:38 +03:00
parent bd07c49755
commit 4307262f6e
8 changed files with 206 additions and 77 deletions

View File

@ -70,6 +70,9 @@ void Core::NetworkAccess::start()
{
if (!running) {
manager = new QNetworkAccessManager();
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
manager->setTransferTimeout();
#endif
storage.open();
running = true;
}
@ -99,6 +102,7 @@ void Core::NetworkAccess::onDownloadProgress(qint64 bytesReceived, qint64 bytesT
qDebug() << "an error downloading" << url << ": the request had some progress but seems like no one is waiting for it, skipping";
} else {
Transfer* dwn = itr->second;
if (dwn->success) {
qreal received = bytesReceived;
qreal total = bytesTotal;
qreal progress = received/total;
@ -106,23 +110,47 @@ void Core::NetworkAccess::onDownloadProgress(qint64 bytesReceived, qint64 bytesT
emit loadFileProgress(dwn->messages, progress, false);
}
}
}
void Core::NetworkAccess::onDownloadError(QNetworkReply::NetworkError code)
{
qDebug() << "DEBUG: DOWNLOAD ERROR";
QNetworkReply* rpl = static_cast<QNetworkReply*>(sender());
qDebug() << rpl->errorString();
QString url = rpl->url().toString();
std::map<QString, Transfer*>::const_iterator itr = downloads.find(url);
if (itr == downloads.end()) {
qDebug() << "an error downloading" << url << ": the request is reporting an error but seems like no one is waiting for it, skipping";
} else {
QString errorText = getErrorText(code);
if (errorText.size() > 0) {
//if (errorText.size() > 0) {
itr->second->success = false;
Transfer* dwn = itr->second;
emit loadFileError(dwn->messages, errorText, false);
//}
}
}
void Core::NetworkAccess::onDownloadSSLError(const QList<QSslError>& errors)
{
qDebug() << "DEBUG: DOWNLOAD SSL ERRORS";
for (const QSslError& err : errors) {
qDebug() << err.errorString();
}
QNetworkReply* rpl = static_cast<QNetworkReply*>(sender());
QString url = rpl->url().toString();
std::map<QString, Transfer*>::const_iterator itr = downloads.find(url);
if (itr == downloads.end()) {
qDebug() << "an SSL error downloading" << url << ": the request is reporting an error but seems like no one is waiting for it, skipping";
} else {
//if (errorText.size() > 0) {
itr->second->success = false;
Transfer* dwn = itr->second;
emit loadFileError(dwn->messages, "SSL errors occured", false);
//}
}
}
QString Core::NetworkAccess::getErrorText(QNetworkReply::NetworkError code)
{
@ -146,7 +174,11 @@ QString Core::NetworkAccess::getErrorText(QNetworkReply::NetworkError code)
errorText = "Connection was closed because it timed out";
break;
case QNetworkReply::OperationCanceledError:
//this means I closed it myself by abort() or close(), don't think I need to notify here
//this means I closed it myself by abort() or close()
//I don't call them directory, but this is the error code
//Qt returns when it can not resume donwload after the network failure
//or when the download is canceled by the timout;
errorText = "Connection lost";
break;
case QNetworkReply::SslHandshakeFailedError:
errorText = "Security error"; //TODO need to handle sslErrors signal to get a better description here
@ -247,6 +279,7 @@ QString Core::NetworkAccess::getErrorText(QNetworkReply::NetworkError code)
void Core::NetworkAccess::onDownloadFinished()
{
qDebug() << "DEBUG: DOWNLOAD FINISHED";
QNetworkReply* rpl = static_cast<QNetworkReply*>(sender());
QString url = rpl->url().toString();
std::map<QString, Transfer*>::const_iterator itr = downloads.find(url);
@ -256,11 +289,14 @@ void Core::NetworkAccess::onDownloadFinished()
Transfer* dwn = itr->second;
if (dwn->success) {
qDebug() << "download success for" << url;
QString err;
QStringList hops = url.split("/");
QString fileName = hops.back();
QString jid;
if (dwn->messages.size() > 0) {
jid = dwn->messages.front().jid;
} else {
qDebug() << "An attempt to save the file but it doesn't seem to belong to any message, download is definately going to be broken";
}
QString path = prepareDirectory(jid);
if (path.size() > 0) {
@ -274,15 +310,16 @@ void Core::NetworkAccess::onDownloadFinished()
qDebug() << "file" << path << "was successfully downloaded";
} else {
qDebug() << "couldn't save file" << path;
path = QString();
err = "Error opening file to write:" + file.errorString();
}
} else {
err = "Couldn't prepare a directory for file";
}
if (path.size() > 0) {
emit downloadFileComplete(dwn->messages, path);
} else {
//TODO do I need to handle the failure here or it's already being handled in error?
//emit loadFileError(dwn->messages, path, false);
emit loadFileError(dwn->messages, "Error saving file " + url + "; " + err, false);
}
}
@ -298,6 +335,7 @@ void Core::NetworkAccess::startDownload(const std::list<Shared::MessageInfo>& ms
QNetworkRequest req(url);
dwn->reply = manager->get(req);
connect(dwn->reply, &QNetworkReply::downloadProgress, this, &NetworkAccess::onDownloadProgress);
connect(dwn->reply, &QNetworkReply::sslErrors, this, &NetworkAccess::onDownloadSSLError);
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
connect(dwn->reply, qOverload<QNetworkReply::NetworkError>(&QNetworkReply::errorOccurred), this, &NetworkAccess::onDownloadError);
#else
@ -317,11 +355,11 @@ void Core::NetworkAccess::onUploadError(QNetworkReply::NetworkError code)
qDebug() << "an error uploading" << url << ": the request is reporting an error but there is no record of it being uploading, ignoring";
} else {
QString errorText = getErrorText(code);
if (errorText.size() > 0) {
//if (errorText.size() > 0) {
itr->second->success = false;
Transfer* upl = itr->second;
emit loadFileError(upl->messages, errorText, true);
}
//}
//TODO deletion?
}
@ -360,6 +398,7 @@ void Core::NetworkAccess::onUploadProgress(qint64 bytesReceived, qint64 bytesTot
qDebug() << "an error downloading" << url << ": the request had some progress but seems like no one is waiting for it, skipping";
} else {
Transfer* upl = itr->second;
if (upl->success) {
qreal received = bytesReceived;
qreal total = bytesTotal;
qreal progress = received/total;
@ -367,6 +406,7 @@ void Core::NetworkAccess::onUploadProgress(qint64 bytesReceived, qint64 bytesTot
emit loadFileProgress(upl->messages, progress, true);
}
}
}
QString Core::NetworkAccess::getFileRemoteUrl(const QString& path)
{

View File

@ -75,6 +75,7 @@ private:
private slots:
void onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal);
void onDownloadError(QNetworkReply::NetworkError code);
void onDownloadSSLError(const QList<QSslError> &errors);
void onDownloadFinished();
void onUploadProgress(qint64 bytesReceived, qint64 bytesTotal);
void onUploadError(QNetworkReply::NetworkError code);

View File

@ -45,6 +45,8 @@ Models::MessageFeed::MessageFeed(const Element* ri, QObject* parent):
syncState(incomplete),
uploads(),
downloads(),
failedDownloads(),
failedUploads(),
unreadMessages(new std::set<QString>()),
observersAmount(0)
{
@ -142,6 +144,18 @@ void Models::MessageFeed::changeMessage(const QString& id, const QMap<QString, Q
}
}
Err::const_iterator eitr = failedDownloads.find(id);
if (eitr != failedDownloads.end()) {
failedDownloads.erase(eitr);
changeRoles.insert(MessageRoles::Attach);
} else {
eitr = failedUploads.find(id);
if (eitr != failedUploads.end()) {
failedUploads.erase(eitr);
changeRoles.insert(MessageRoles::Attach);
}
}
QVector<int> cr;
for (MessageRoles role : changeRoles) {
cr.push_back(role);
@ -421,6 +435,7 @@ bool Models::MessageFeed::sentByMe(const Shared::Message& msg) const
Models::Attachment Models::MessageFeed::fillAttach(const Shared::Message& msg) const
{
::Models::Attachment att;
QString id = msg.getId();
att.localPath = msg.getAttachPath();
att.remotePath = msg.getOutOfBandUrl();
@ -429,7 +444,12 @@ Models::Attachment Models::MessageFeed::fillAttach(const Shared::Message& msg) c
if (att.localPath.size() == 0) {
att.state = none;
} else {
Progress::const_iterator itr = uploads.find(msg.getId());
Err::const_iterator eitr = failedUploads.find(id);
if (eitr != failedUploads.end()) {
att.state = errorUpload;
att.error = eitr->second;
} else {
Progress::const_iterator itr = uploads.find(id);
if (itr == uploads.end()) {
att.state = local;
} else {
@ -437,15 +457,22 @@ Models::Attachment Models::MessageFeed::fillAttach(const Shared::Message& msg) c
att.progress = itr->second;
}
}
}
} else {
if (att.localPath.size() == 0) {
Progress::const_iterator itr = downloads.find(msg.getId());
Err::const_iterator eitr = failedDownloads.find(id);
if (eitr != failedDownloads.end()) {
att.state = errorDownload;
att.error = eitr->second;
} else {
Progress::const_iterator itr = downloads.find(id);
if (itr == downloads.end()) {
att.state = remote;
} else {
att.state = downloading;
att.progress = itr->second;
}
}
} else {
att.state = ready;
}
@ -456,12 +483,19 @@ Models::Attachment Models::MessageFeed::fillAttach(const Shared::Message& msg) c
void Models::MessageFeed::downloadAttachment(const QString& messageId)
{
bool notify = false;
Err::const_iterator eitr = failedDownloads.find(messageId);
if (eitr != failedDownloads.end()) {
failedDownloads.erase(eitr);
notify = true;
}
QModelIndex ind = modelIndexById(messageId);
if (ind.isValid()) {
std::pair<Progress::iterator, bool> progressPair = downloads.insert(std::make_pair(messageId, 0));
if (progressPair.second) { //Only to take action if we weren't already downloading it
Shared::Message* msg = static_cast<Shared::Message*>(ind.internalPointer());
emit dataChanged(ind, ind, {MessageRoles::Attach});
notify = true;
emit fileDownloadRequest(msg->getOutOfBandUrl());
} else {
qDebug() << "Attachment download for message with id" << messageId << "is already in progress, skipping";
@ -469,32 +503,55 @@ void Models::MessageFeed::downloadAttachment(const QString& messageId)
} else {
qDebug() << "An attempt to download an attachment for the message that doesn't exist. ID:" << messageId;
}
}
void Models::MessageFeed::uploadAttachment(const QString& messageId)
{
qDebug() << "request to upload attachment of the message" << messageId;
if (notify) {
emit dataChanged(ind, ind, {MessageRoles::Attach});
}
}
bool Models::MessageFeed::registerUpload(const QString& messageId)
{
return uploads.insert(std::make_pair(messageId, 0)).second;
bool success = uploads.insert(std::make_pair(messageId, 0)).second;
QVector<int> roles({});
Err::const_iterator eitr = failedUploads.find(messageId);
if (eitr != failedUploads.end()) {
failedUploads.erase(eitr);
roles.push_back(MessageRoles::Attach);
} else if (success) {
roles.push_back(MessageRoles::Attach);
}
QModelIndex ind = modelIndexById(messageId);
emit dataChanged(ind, ind, roles);
return success;
}
void Models::MessageFeed::fileProgress(const QString& messageId, qreal value, bool up)
{
Progress* pr = 0;
Err* err = 0;
if (up) {
pr = &uploads;
err = &failedUploads;
} else {
pr = &downloads;
err = &failedDownloads;
}
QVector<int> roles({});
Err::const_iterator eitr = err->find(messageId);
if (eitr != err->end() && value != 1) { //like I want to clear this state when the download is started anew
err->erase(eitr);
roles.push_back(MessageRoles::Attach);
}
Progress::iterator itr = pr->find(messageId);
if (itr != pr->end()) {
itr->second = value;
QModelIndex ind = modelIndexById(messageId);
emit dataChanged(ind, ind); //the type of the attach didn't change, so, there is no need to relayout, there is no role in event
emit dataChanged(ind, ind, roles);
}
}
@ -505,7 +562,29 @@ void Models::MessageFeed::fileComplete(const QString& messageId, bool up)
void Models::MessageFeed::fileError(const QString& messageId, const QString& error, bool up)
{
//TODO
Err* failed;
Progress* loads;
if (up) {
failed = &failedUploads;
loads = &uploads;
} else {
failed = &failedDownloads;
loads = &downloads;
}
Progress::iterator pitr = loads->find(messageId);
if (pitr != loads->end()) {
loads->erase(pitr);
}
std::pair<Err::iterator, bool> pair = failed->insert(std::make_pair(messageId, error));
if (!pair.second) {
pair.first->second = error;
}
QModelIndex ind = modelIndexById(messageId);
if (ind.isValid()) {
emit dataChanged(ind, ind, {MessageRoles::Attach});
}
}
void Models::MessageFeed::incrementObservers()
@ -533,19 +612,18 @@ QModelIndex Models::MessageFeed::modelIndexById(const QString& id) const
QModelIndex Models::MessageFeed::modelIndexByTime(const QString& id, const QDateTime& time) const
{
if (indexByTime.size() > 0) {
StorageByTime::const_iterator tItr = indexByTime.upper_bound(time);
StorageByTime::const_iterator tBeg = indexByTime.begin();
StorageByTime::const_iterator tEnd = indexByTime.end();
StorageByTime::const_iterator tItr = indexByTime.lower_bound(time);
StorageByTime::const_iterator tEnd = indexByTime.upper_bound(time);
bool found = false;
while (tItr != tBeg) {
if (tItr != tEnd && id == (*tItr)->getId()) {
while (tItr != tEnd) {
if (id == (*tItr)->getId()) {
found = true;
break;
}
--tItr;
++tItr;
}
if (found && tItr != tEnd && id == (*tItr)->getId()) {
if (found) {
int position = indexByTime.rank(tItr);
return createIndex(position, 0, *tItr);
}
@ -566,7 +644,7 @@ void Models::MessageFeed::reportLocalPathInvalid(const QString& messageId)
emit localPathInvalid(msg->getAttachPath());
//gonna change the message in current model right away, to prevent spam on each attemt to draw element
//gonna change the message in current model right away, to prevent spam on each attempt to draw element
QModelIndex index = modelIndexByTime(messageId, msg->getTime());
msg->setAttachPath("");

View File

@ -65,7 +65,6 @@ public:
void responseArchive(const std::list<Shared::Message> list, bool last);
void downloadAttachment(const QString& messageId);
void uploadAttachment(const QString& messageId);
bool registerUpload(const QString& messageId);
void reportLocalPathInvalid(const QString& messageId);
@ -148,12 +147,16 @@ private:
SyncState syncState;
typedef std::map<QString, qreal> Progress;
typedef std::map<QString, QString> Err;
Progress uploads;
Progress downloads;
Err failedDownloads;
Err failedUploads;
std::set<QString>* unreadMessages;
uint16_t observersAmount;
static const QHash<int, QByteArray> roles;
};
@ -173,6 +176,7 @@ struct Attachment {
qreal progress;
QString localPath;
QString remotePath;
QString error;
};
struct FeedItem {

View File

@ -394,16 +394,11 @@ void FeedView::setModel(QAbstractItemModel* p_model)
}
}
void FeedView::onMessageButtonPushed(const QString& messageId, bool download)
void FeedView::onMessageButtonPushed(const QString& messageId)
{
if (specialModel) {
Models::MessageFeed* feed = static_cast<Models::MessageFeed*>(model());
if (download) {
feed->downloadAttachment(messageId);
} else {
feed->uploadAttachment(messageId);
}
}
}

View File

@ -58,7 +58,7 @@ protected slots:
void rowsInserted(const QModelIndex & parent, int start, int end) override;
void verticalScrollbarValueChanged(int value) override;
void dataChanged(const QModelIndex & topLeft, const QModelIndex & bottomRight, const QVector<int> & roles) override;
void onMessageButtonPushed(const QString& messageId, bool download);
void onMessageButtonPushed(const QString& messageId);
void onMessageInvalidPath(const QString& messageId);
void onModelSyncStateChange(Models::MessageFeed::SyncState state);

View File

@ -137,19 +137,39 @@ void MessageDelegate::paint(QPainter* painter, const QStyleOptionViewItem& optio
clearHelperWidget(data); //i can't imagine the situation where it's gonna be needed
break; //but it's a possible performance problem
case Models::uploading:
paintPreview(data, painter, opt);
case Models::downloading:
paintBar(getBar(data), painter, data.sentByMe, opt);
break;
case Models::remote:
case Models::local:
paintButton(getButton(data), painter, data.sentByMe, opt);
break;
case Models::ready:
case Models::local:
clearHelperWidget(data);
paintPreview(data, painter, opt);
break;
case Models::errorDownload:
case Models::errorUpload:
case Models::errorDownload: {
paintButton(getButton(data), painter, data.sentByMe, opt);
painter->setFont(dateFont);
QColor q = painter->pen().color();
q.setAlpha(180);
painter->setPen(q);
painter->drawText(opt.rect, opt.displayAlignment, data.attach.error, &rect);
opt.rect.adjust(0, rect.height() + textMargin, 0, 0);
}
break;
case Models::errorUpload:{
clearHelperWidget(data);
paintPreview(data, painter, opt);
painter->setFont(dateFont);
QColor q = painter->pen().color();
q.setAlpha(180);
painter->setPen(q);
painter->drawText(opt.rect, opt.displayAlignment, data.attach.error, &rect);
opt.rect.adjust(0, rect.height() + textMargin, 0, 0);
}
break;
}
painter->restore();
@ -212,18 +232,24 @@ QSize MessageDelegate::sizeHint(const QStyleOptionViewItem& option, const QModel
case Models::none:
break;
case Models::uploading:
messageSize.rheight() += calculateAttachSize(attach.localPath, messageRect).height() + textMargin;
case Models::downloading:
messageSize.rheight() += barHeight + textMargin;
break;
case Models::remote:
case Models::local:
messageSize.rheight() += buttonHeight + textMargin;
break;
case Models::ready:
case Models::local:
messageSize.rheight() += calculateAttachSize(attach.localPath, messageRect).height() + textMargin;
break;
case Models::errorDownload:
messageSize.rheight() += buttonHeight + textMargin;
messageSize.rheight() += dateMetrics.boundingRect(messageRect, Qt::TextWordWrap, attach.error).size().height() + textMargin;
break;
case Models::errorUpload:
messageSize.rheight() += calculateAttachSize(attach.localPath, messageRect).height() + textMargin;
messageSize.rheight() += dateMetrics.boundingRect(messageRect, Qt::TextWordWrap, attach.error).size().height() + textMargin;
break;
}
@ -356,15 +382,7 @@ QPushButton * MessageDelegate::getButton(const Models::FeedItem& data) const
std::map<QString, FeedButton*>::const_iterator itr = buttons->find(data.id);
FeedButton* result = 0;
if (itr != buttons->end()) {
if (
(data.attach.state == Models::remote && itr->second->download) ||
(data.attach.state == Models::local && !itr->second->download)
) {
result = itr->second;
} else {
delete itr->second;
buttons->erase(itr);
}
} else {
std::map<QString, QProgressBar*>::const_iterator barItr = bars->find(data.id);
if (barItr != bars->end()) {
@ -376,13 +394,7 @@ QPushButton * MessageDelegate::getButton(const Models::FeedItem& data) const
if (result == 0) {
result = new FeedButton();
result->messageId = data.id;
if (data.attach.state == Models::remote) {
result->setText(QCoreApplication::translate("MessageLine", "Download"));
result->download = true;
} else {
result->setText(QCoreApplication::translate("MessageLine", "Upload"));
result->download = false;
}
buttons->insert(std::make_pair(data.id, result));
connect(result, &QPushButton::clicked, this, &MessageDelegate::onButtonPushed);
}
@ -529,7 +541,7 @@ void MessageDelegate::endClearWidgets()
void MessageDelegate::onButtonPushed() const
{
FeedButton* btn = static_cast<FeedButton*>(sender());
emit buttonPushed(btn->messageId, btn->download);
emit buttonPushed(btn->messageId);
}
void MessageDelegate::clearHelperWidget(const Models::FeedItem& data) const

View File

@ -55,7 +55,7 @@ public:
void beginClearWidgets();
signals:
void buttonPushed(const QString& messageId, bool download) const;
void buttonPushed(const QString& messageId) const;
void invalidPath(const QString& messageId) const;
protected:
@ -77,7 +77,6 @@ private:
class FeedButton : public QPushButton {
public:
QString messageId;
bool download;
};
QFont bodyFont;