Show more informative error message when VFS hydration fails. Display a popup and put an error into activity list. More detailed logs.

Signed-off-by: alex-z <blackslayer4@gmail.com>
This commit is contained in:
alex-z 2023-10-13 19:21:17 +02:00
parent bac61c7a6b
commit 727cd797ff
10 changed files with 295 additions and 3 deletions

View File

@ -265,6 +265,8 @@ signals:
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.

View File

@ -44,6 +44,7 @@ set(client_UI_SRCS
passwordinputdialog.ui
proxyauthdialog.ui
mnemonicdialog.ui
vfsdownloaderrordialog.ui
wizard/flow2authwidget.ui
wizard/owncloudadvancedsetuppage.ui
wizard/owncloudconnectionmethoddialog.ui
@ -158,6 +159,8 @@ set(client_SRCS
thumbnailjob.cpp
userinfo.h
userinfo.cpp
vfsdownloaderrordialog.h
vfsdownloaderrordialog.cpp
accountstate.h
accountstate.cpp
addcertificatedialog.h

View File

@ -37,6 +37,7 @@
#include "common/vfs.h"
#include "creds/abstractcredentials.h"
#include "settingsdialog.h"
#include "vfsdownloaderrordialog.h"
#include <QTimer>
#include <QUrl>
@ -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()) {

View File

@ -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:

View File

@ -0,0 +1,35 @@
/*
* Copyright (C) 2023 by Oleksandr Zolotov <alex@nextcloud.com>
*
* 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;
}

View File

@ -0,0 +1,35 @@
/*
* Copyright (C) 2023 by Oleksandr Zolotov <alex@nextcloud.com>
*
* 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 <QDialog>
#include <QScopedPointer>
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::VfsDownloadErrorDialog> _ui;
};
}

View File

@ -0,0 +1,166 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>OCC::VfsDownloadErrorDialog</class>
<widget class="QDialog" name="OCC::VfsDownloadErrorDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>397</width>
<height>155</height>
</rect>
</property>
<property name="windowTitle">
<string>Download error</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum>
</property>
<item>
<widget class="QLabel" name="descriptionLabel">
<property name="text">
<string>Error downloading</string>
</property>
<property name="textFormat">
<enum>Qt::PlainText</enum>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
<property name="wordWrap">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="explanationLabel">
<property name="text">
<string>could not be downloaded</string>
</property>
<property name="textFormat">
<enum>Qt::PlainText</enum>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
<property name="wordWrap">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="moreDetailsButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>&gt; More details</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="moreDetailsLabel">
<property name="text">
<string>More details</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="errorLabel">
<property name="palette">
<palette>
<active>
<colorrole role="WindowText">
<brush brushstyle="SolidPattern">
<color alpha="200">
<red>255</red>
<green>0</green>
<blue>0</blue>
</color>
</brush>
</colorrole>
</active>
<inactive>
<colorrole role="WindowText">
<brush brushstyle="SolidPattern">
<color alpha="200">
<red>255</red>
<green>0</green>
<blue>0</blue>
</color>
</brush>
</colorrole>
</inactive>
<disabled>
<colorrole role="WindowText">
<brush brushstyle="SolidPattern">
<color alpha="115">
<red>255</red>
<green>255</green>
<blue>255</blue>
</color>
</brush>
</colorrole>
</disabled>
</palette>
</property>
<property name="text">
<string/>
</property>
<property name="textFormat">
<enum>Qt::PlainText</enum>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>moreDetailsButton</sender>
<signal>clicked()</signal>
<receiver>moreDetailsLabel</receiver>
<slot>show()</slot>
<hints>
<hint type="sourcelabel">
<x>47</x>
<y>112</y>
</hint>
<hint type="destinationlabel">
<x>198</x>
<y>141</y>
</hint>
</hints>
</connection>
<connection>
<sender>moreDetailsButton</sender>
<signal>clicked()</signal>
<receiver>moreDetailsButton</receiver>
<slot>hide()</slot>
<hints>
<hint type="sourcelabel">
<x>47</x>
<y>63</y>
</hint>
<hint type="destinationlabel">
<x>47</x>
<y>63</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -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;
}

View File

@ -71,6 +71,10 @@ public:
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 @@ private:
QLocalSocket *_signalSocket = nullptr;
GETFileJob *_job = nullptr;
Status _status = Success;
int _errorCode = 0;
int _statusCode = 0;
QString _errorString;
};
} // namespace OCC

View File

@ -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)