diff --git a/src/common/vfs.h b/src/common/vfs.h index 39bf0c03a44c..17a4c48e7aeb 100644 --- a/src/common/vfs.h +++ b/src/common/vfs.h @@ -265,6 +265,8 @@ public slots: void beginHydrating(); /// Emitted when the hydration ends void doneHydrating(); + // Emitted when hydration fails + void failureHydrating(int errorCode, int statusCode, const QString &errorString, const QString &fileName); protected: /** Setup the plugin for the folder. diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 58320e1cecb9..52cec7946023 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -45,6 +45,7 @@ set(client_UI_SRCS passwordinputdialog.ui proxyauthdialog.ui mnemonicdialog.ui + vfsdownloaderrordialog.ui wizard/flow2authwidget.ui wizard/owncloudadvancedsetuppage.ui wizard/owncloudconnectionmethoddialog.ui @@ -159,6 +160,8 @@ set(client_SRCS thumbnailjob.cpp userinfo.h userinfo.cpp + vfsdownloaderrordialog.h + vfsdownloaderrordialog.cpp accountstate.h accountstate.cpp addcertificatedialog.h diff --git a/src/gui/folder.cpp b/src/gui/folder.cpp index e4984f53a7c7..dc37d71d1889 100644 --- a/src/gui/folder.cpp +++ b/src/gui/folder.cpp @@ -37,6 +37,7 @@ #include "common/vfs.h" #include "creds/abstractcredentials.h" #include "settingsdialog.h" +#include "vfsdownloaderrordialog.h" #include #include @@ -506,6 +507,7 @@ void Folder::startVfs() connect(_vfs.data(), &Vfs::beginHydrating, this, &Folder::slotHydrationStarts); connect(_vfs.data(), &Vfs::doneHydrating, this, &Folder::slotHydrationDone); + connect(_vfs.data(), &Vfs::failureHydrating, this, &Folder::slotHydrationFailed); connect(&_engine->syncFileStatusTracker(), &SyncFileStatusTracker::fileStatusChanged, _vfs.data(), &Vfs::fileStatusChanged); @@ -1510,6 +1512,22 @@ void Folder::slotHydrationDone() emit syncStateChange(); } +void Folder::slotHydrationFailed(int errorCode, int statusCode, const QString &errorString, const QString &fileName) +{ + _syncResult.setStatus(SyncResult::Error); + const auto errorMessageDetails = tr("Virtual file download failed with code \"%1\", status \"%2\" and error message \"%3\"") + .arg(errorCode) + .arg(statusCode) + .arg(errorString); + _syncResult.appendErrorString(errorMessageDetails); + + const auto errorMessageBox = new VfsDownloadErrorDialog(fileName, errorMessageDetails); + errorMessageBox->setAttribute(Qt::WA_DeleteOnClose); + errorMessageBox->show(); + errorMessageBox->activateWindow(); + errorMessageBox->raise(); +} + void Folder::slotCapabilitiesChanged() { if (_accountState->account()->capabilities().filesLockAvailable()) { diff --git a/src/gui/folder.h b/src/gui/folder.h index 4b4e6e15a3b6..3398216dfa92 100644 --- a/src/gui/folder.h +++ b/src/gui/folder.h @@ -449,6 +449,9 @@ private slots: /** Unblocks normal sync operation */ void slotHydrationDone(); + /* Hydration failed, perform required steps to notify user */ + void slotHydrationFailed(int errorCode, int statusCode, const QString &errorString, const QString &fileName); + void slotCapabilitiesChanged(); private: diff --git a/src/gui/vfsdownloaderrordialog.cpp b/src/gui/vfsdownloaderrordialog.cpp new file mode 100644 index 000000000000..b80df5e15639 --- /dev/null +++ b/src/gui/vfsdownloaderrordialog.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2023 by Oleksandr Zolotov + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program 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. + */ +#include "vfsdownloaderrordialog.h" +#include "ui_vfsdownloaderrordialog.h" + +namespace OCC { + +VfsDownloadErrorDialog::VfsDownloadErrorDialog(const QString &fileName, const QString &errorMessage, QWidget *parent) + : QDialog(parent) + , _ui(new Ui::VfsDownloadErrorDialog) +{ + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + + _ui->setupUi(this); + _ui->descriptionLabel->setText(tr("Error downloading %1").arg(fileName)); + _ui->explanationLabel->setText(tr("%1 could not be downloaded.").arg(fileName)); + _ui->moreDetailsLabel->setText(errorMessage); + _ui->moreDetailsLabel->setVisible(false); + + connect(_ui->buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); +} + +VfsDownloadErrorDialog::~VfsDownloadErrorDialog() = default; +} diff --git a/src/gui/vfsdownloaderrordialog.h b/src/gui/vfsdownloaderrordialog.h new file mode 100644 index 000000000000..0410cd9c9cce --- /dev/null +++ b/src/gui/vfsdownloaderrordialog.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2023 by Oleksandr Zolotov + * + * 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 2 of the License, or + * (at your option) any later version. + * + * This program 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. + */ +#pragma once +#include +#include + +namespace OCC { + +namespace Ui { + class VfsDownloadErrorDialog; +} + +class VfsDownloadErrorDialog : public QDialog +{ + Q_OBJECT + +public: + explicit VfsDownloadErrorDialog(const QString &fileName, const QString &errorMessage, QWidget *parent = nullptr); + ~VfsDownloadErrorDialog() override; + +private: + QScopedPointer _ui; +}; +} diff --git a/src/gui/vfsdownloaderrordialog.ui b/src/gui/vfsdownloaderrordialog.ui new file mode 100644 index 000000000000..a963db70e0a7 --- /dev/null +++ b/src/gui/vfsdownloaderrordialog.ui @@ -0,0 +1,166 @@ + + + OCC::VfsDownloadErrorDialog + + + + 0 + 0 + 397 + 155 + + + + Download error + + + + QLayout::SetDefaultConstraint + + + + + Error downloading + + + Qt::PlainText + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + false + + + + + + + could not be downloaded + + + Qt::PlainText + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + false + + + + + + + + 0 + 0 + + + + > More details + + + + + + + More details + + + true + + + + + + + QDialogButtonBox::Ok + + + + + + + + + + + + 255 + 0 + 0 + + + + + + + + + 255 + 0 + 0 + + + + + + + + + 255 + 255 + 255 + + + + + + + + + + + Qt::PlainText + + + + + + + + + moreDetailsButton + clicked() + moreDetailsLabel + show() + + + 47 + 112 + + + 198 + 141 + + + + + moreDetailsButton + clicked() + moreDetailsButton + hide() + + + 47 + 63 + + + 47 + 63 + + + + + diff --git a/src/libsync/vfs/cfapi/hydrationjob.cpp b/src/libsync/vfs/cfapi/hydrationjob.cpp index daaf657394ee..58e30c22b571 100644 --- a/src/libsync/vfs/cfapi/hydrationjob.cpp +++ b/src/libsync/vfs/cfapi/hydrationjob.cpp @@ -116,6 +116,21 @@ OCC::HydrationJob::Status OCC::HydrationJob::status() const return _status; } +int OCC::HydrationJob::errorCode() const +{ + return _errorCode; +} + +int OCC::HydrationJob::statusCode() const +{ + return _statusCode; +} + +QString OCC::HydrationJob::errorString() const +{ + return _errorString; +} + void OCC::HydrationJob::start() { Q_ASSERT(_account); @@ -334,16 +349,21 @@ void OCC::HydrationJob::finalize(OCC::VfsCfApi *vfs) void OCC::HydrationJob::onGetFinished() { - qCInfo(lcHydration) << "GETFileJob finished" << _requestId << _folderPath << _job->reply()->error(); + _errorCode = _job->reply()->error(); + _statusCode = _job->reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + _errorString = _job->reply()->errorString(); - const auto isGetJobResultError = _job->reply()->error(); + qCInfo(lcHydration) << "GETFileJob finished" << _requestId << _folderPath << _errorCode << _statusCode << _errorString; // GETFileJob deletes itself after this signal was handled _job = nullptr; if (_isCancelled) { + _errorCode = 0; + _statusCode = 0; + _errorString.clear(); return; } - if (isGetJobResultError) { + if (_errorCode) { emitFinished(Error); return; } diff --git a/src/libsync/vfs/cfapi/hydrationjob.h b/src/libsync/vfs/cfapi/hydrationjob.h index c13cd7b5021a..153da71f913f 100644 --- a/src/libsync/vfs/cfapi/hydrationjob.h +++ b/src/libsync/vfs/cfapi/hydrationjob.h @@ -71,6 +71,10 @@ class HydrationJob : public QObject Status status() const; + [[nodiscard]] int errorCode() const; + [[nodiscard]] int statusCode() const; + [[nodiscard]] QString errorString() const; + void start(); void cancel(); void finalize(OCC::VfsCfApi *vfs); @@ -114,6 +118,9 @@ public slots: QLocalSocket *_signalSocket = nullptr; GETFileJob *_job = nullptr; Status _status = Success; + int _errorCode = 0; + int _statusCode = 0; + QString _errorString; }; } // namespace OCC diff --git a/src/libsync/vfs/cfapi/vfs_cfapi.cpp b/src/libsync/vfs/cfapi/vfs_cfapi.cpp index 7763a2fd85a4..d622913fd6d1 100644 --- a/src/libsync/vfs/cfapi/vfs_cfapi.cpp +++ b/src/libsync/vfs/cfapi/vfs_cfapi.cpp @@ -448,6 +448,9 @@ void VfsCfApi::onHydrationJobFinished(HydrationJob *job) Q_ASSERT(d->hydrationJobs.contains(job)); qCInfo(lcCfApi) << "Hydration job finished" << job->requestId() << job->folderPath() << job->status(); emit hydrationRequestFinished(job->requestId()); + if (!job->errorString().isEmpty()) { + emit failureHydrating(job->errorCode(), job->statusCode(), job->errorString(), job->folderPath()); + } } int VfsCfApi::finalizeHydrationJob(const QString &requestId)