Improve error handling
Allow viewing request URL and response of internal errors.
This commit is contained in:
parent
c8b68bc7c7
commit
e52a2a6ef6
|
@ -202,10 +202,24 @@ void Application::handleResponse()
|
|||
}
|
||||
}
|
||||
|
||||
void Application::handleError(const QString &message)
|
||||
void Application::handleError(
|
||||
const QString &message, SyncthingErrorCategory category, int networkError, const QNetworkRequest &request, const QByteArray &response)
|
||||
{
|
||||
VAR_UNUSED(category)
|
||||
VAR_UNUSED(networkError)
|
||||
eraseLine(cout);
|
||||
cerr << "\rError: " << message.toLocal8Bit().data() << endl;
|
||||
cerr << '\n';
|
||||
setStyle(cerr, Color::Red, ColorContext::Foreground, TextAttribute::Bold);
|
||||
cerr << "\rError: ";
|
||||
resetStyle(cerr);
|
||||
cerr << message.toLocal8Bit().data();
|
||||
const QUrl url(request.url());
|
||||
if (!url.isEmpty()) {
|
||||
cerr << "\nRequest: " << url.toString(QUrl::PrettyDecoded).toLocal8Bit().data();
|
||||
}
|
||||
if (!response.isEmpty()) {
|
||||
cerr << "\nResponse:\n" << response.data() << endl;
|
||||
}
|
||||
QCoreApplication::exit(-3);
|
||||
}
|
||||
|
||||
|
|
|
@ -26,7 +26,8 @@ public:
|
|||
private slots:
|
||||
void handleStatusChanged(Data::SyncthingStatus newStatus);
|
||||
void handleResponse();
|
||||
void handleError(const QString &message);
|
||||
void handleError(
|
||||
const QString &message, Data::SyncthingErrorCategory category, int networkError, const QNetworkRequest &request, const QByteArray &response);
|
||||
void findRelevantDirsAndDevs();
|
||||
void findRelevantDirsAndDevs(OperationType operationType);
|
||||
bool findPwd();
|
||||
|
|
|
@ -864,7 +864,7 @@ bool SyncthingConnection::applySettings(SyncthingConnectionSettings &connectionS
|
|||
*/
|
||||
void SyncthingConnection::readConfig()
|
||||
{
|
||||
auto *reply = static_cast<QNetworkReply *>(sender());
|
||||
auto *const reply = static_cast<QNetworkReply *>(sender());
|
||||
reply->deleteLater();
|
||||
if (reply == m_configReply) {
|
||||
m_configReply = nullptr;
|
||||
|
@ -872,8 +872,9 @@ void SyncthingConnection::readConfig()
|
|||
|
||||
switch (reply->error()) {
|
||||
case QNetworkReply::NoError: {
|
||||
const QByteArray response(reply->readAll());
|
||||
QJsonParseError jsonError;
|
||||
const QJsonDocument replyDoc = QJsonDocument::fromJson(reply->readAll(), &jsonError);
|
||||
const QJsonDocument replyDoc = QJsonDocument::fromJson(response, &jsonError);
|
||||
if (jsonError.error == QJsonParseError::NoError) {
|
||||
m_rawConfig = replyDoc.object();
|
||||
emit newConfig(m_rawConfig);
|
||||
|
@ -884,14 +885,14 @@ void SyncthingConnection::readConfig()
|
|||
continueConnecting();
|
||||
}
|
||||
} else {
|
||||
emit error(tr("Unable to parse Syncthing config: ") + jsonError.errorString(), SyncthingErrorCategory::Parsing, QNetworkReply::NoError);
|
||||
emitError(tr("Unable to parse Syncthing config: "), jsonError, reply, response);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case QNetworkReply::OperationCanceledError:
|
||||
return; // intended, not an error
|
||||
default:
|
||||
emit error(tr("Unable to request Syncthing config: ") + reply->errorString(), SyncthingErrorCategory::OverallConnection, reply->error());
|
||||
emitError(tr("Unable to request Syncthing config: "), SyncthingErrorCategory::OverallConnection, reply);
|
||||
setStatus(SyncthingStatus::Disconnected);
|
||||
if (m_autoReconnectTimer.interval()) {
|
||||
m_autoReconnectTimer.start();
|
||||
|
@ -962,7 +963,7 @@ void SyncthingConnection::readDevs(const QJsonArray &devs)
|
|||
*/
|
||||
void SyncthingConnection::readStatus()
|
||||
{
|
||||
auto *reply = static_cast<QNetworkReply *>(sender());
|
||||
auto *const reply = static_cast<QNetworkReply *>(sender());
|
||||
reply->deleteLater();
|
||||
if (reply == m_statusReply) {
|
||||
m_statusReply = nullptr;
|
||||
|
@ -970,8 +971,9 @@ void SyncthingConnection::readStatus()
|
|||
|
||||
switch (reply->error()) {
|
||||
case QNetworkReply::NoError: {
|
||||
const QByteArray response(reply->readAll());
|
||||
QJsonParseError jsonError;
|
||||
const QJsonDocument replyDoc = QJsonDocument::fromJson(reply->readAll(), &jsonError);
|
||||
const QJsonDocument replyDoc = QJsonDocument::fromJson(response, &jsonError);
|
||||
if (jsonError.error == QJsonParseError::NoError) {
|
||||
const QJsonObject replyObj(replyDoc.object());
|
||||
const QString myId(replyObj.value(QStringLiteral("myID")).toString());
|
||||
|
@ -993,14 +995,14 @@ void SyncthingConnection::readStatus()
|
|||
continueConnecting();
|
||||
}
|
||||
} else {
|
||||
emit error(tr("Unable to parse Syncthing status: ") + jsonError.errorString(), SyncthingErrorCategory::Parsing, QNetworkReply::NoError);
|
||||
emitError(tr("Unable to parse Syncthing status: "), jsonError, reply, response);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case QNetworkReply::OperationCanceledError:
|
||||
return; // intended, not an error
|
||||
default:
|
||||
emit error(tr("Unable to request Syncthing status: ") + reply->errorString(), SyncthingErrorCategory::OverallConnection, reply->error());
|
||||
emitError(tr("Unable to request Syncthing status: "), SyncthingErrorCategory::OverallConnection, reply);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1009,7 +1011,7 @@ void SyncthingConnection::readStatus()
|
|||
*/
|
||||
void SyncthingConnection::readConnections()
|
||||
{
|
||||
auto *reply = static_cast<QNetworkReply *>(sender());
|
||||
auto *const reply = static_cast<QNetworkReply *>(sender());
|
||||
reply->deleteLater();
|
||||
if (reply == m_connectionsReply) {
|
||||
m_connectionsReply = nullptr;
|
||||
|
@ -1017,8 +1019,9 @@ void SyncthingConnection::readConnections()
|
|||
|
||||
switch (reply->error()) {
|
||||
case QNetworkReply::NoError: {
|
||||
const QByteArray response(reply->readAll());
|
||||
QJsonParseError jsonError;
|
||||
const QJsonDocument replyDoc = QJsonDocument::fromJson(reply->readAll(), &jsonError);
|
||||
const QJsonDocument replyDoc = QJsonDocument::fromJson(response, &jsonError);
|
||||
if (jsonError.error == QJsonParseError::NoError) {
|
||||
const QJsonObject replyObj(replyDoc.object());
|
||||
const QJsonObject totalObj(replyObj.value(QStringLiteral("total")).toObject());
|
||||
|
@ -1081,14 +1084,14 @@ void SyncthingConnection::readConnections()
|
|||
m_trafficPollTimer.start();
|
||||
}
|
||||
} else {
|
||||
emit error(tr("Unable to parse connections: ") + jsonError.errorString(), SyncthingErrorCategory::Parsing, QNetworkReply::NoError);
|
||||
emitError(tr("Unable to parse connections: "), jsonError, reply, response);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case QNetworkReply::OperationCanceledError:
|
||||
return; // intended, not an error
|
||||
default:
|
||||
emit error(tr("Unable to request connections: ") + reply->errorString(), SyncthingErrorCategory::OverallConnection, reply->error());
|
||||
emitError(tr("Unable to request connections: "), SyncthingErrorCategory::OverallConnection, reply);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1097,13 +1100,18 @@ void SyncthingConnection::readConnections()
|
|||
*/
|
||||
void SyncthingConnection::readDirStatistics()
|
||||
{
|
||||
auto *reply = static_cast<QNetworkReply *>(sender());
|
||||
auto *const reply = static_cast<QNetworkReply *>(sender());
|
||||
reply->deleteLater();
|
||||
|
||||
switch (reply->error()) {
|
||||
case QNetworkReply::NoError: {
|
||||
const QByteArray response(reply->readAll());
|
||||
QJsonParseError jsonError;
|
||||
const QJsonDocument replyDoc = QJsonDocument::fromJson(reply->readAll(), &jsonError);
|
||||
const QJsonDocument replyDoc = QJsonDocument::fromJson(response, &jsonError);
|
||||
QTimer::singleShot(7000, [this] {
|
||||
emit this->error(QStringLiteral("some error message"), SyncthingErrorCategory::SpecificRequest, 0,
|
||||
QNetworkRequest(QUrl(QString("http://tadlasjkfefj asdj<sdf jasyöldf"))), QByteArray("asdlkfja saslködfj asölf jaeöfklaj söefkla"));
|
||||
});
|
||||
if (jsonError.error == QJsonParseError::NoError) {
|
||||
const QJsonObject replyObj(replyDoc.object());
|
||||
int index = 0;
|
||||
|
@ -1142,15 +1150,14 @@ void SyncthingConnection::readDirStatistics()
|
|||
++index;
|
||||
}
|
||||
} else {
|
||||
emit error(
|
||||
tr("Unable to parse directory statistics: ") + jsonError.errorString(), SyncthingErrorCategory::Parsing, QNetworkReply::NoError);
|
||||
emitError(tr("Unable to parse directory statistics: "), jsonError, reply, response);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case QNetworkReply::OperationCanceledError:
|
||||
return; // intended, not an error
|
||||
default:
|
||||
emit error(tr("Unable to request directory statistics: ") + reply->errorString(), SyncthingErrorCategory::OverallConnection, reply->error());
|
||||
emitError(tr("Unable to request directory statistics: "), SyncthingErrorCategory::OverallConnection, reply);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1159,13 +1166,14 @@ void SyncthingConnection::readDirStatistics()
|
|||
*/
|
||||
void SyncthingConnection::readDeviceStatistics()
|
||||
{
|
||||
auto *reply = static_cast<QNetworkReply *>(sender());
|
||||
auto *const reply = static_cast<QNetworkReply *>(sender());
|
||||
reply->deleteLater();
|
||||
|
||||
switch (reply->error()) {
|
||||
case QNetworkReply::NoError: {
|
||||
const QByteArray response(reply->readAll());
|
||||
QJsonParseError jsonError;
|
||||
const QJsonDocument replyDoc = QJsonDocument::fromJson(reply->readAll(), &jsonError);
|
||||
const QJsonDocument replyDoc = QJsonDocument::fromJson(response, &jsonError);
|
||||
if (jsonError.error == QJsonParseError::NoError) {
|
||||
const QJsonObject replyObj(replyDoc.object());
|
||||
int index = 0;
|
||||
|
@ -1186,14 +1194,14 @@ void SyncthingConnection::readDeviceStatistics()
|
|||
m_devStatsPollTimer.start();
|
||||
}
|
||||
} else {
|
||||
emit error(tr("Unable to parse device statistics: ") + jsonError.errorString(), SyncthingErrorCategory::Parsing, QNetworkReply::NoError);
|
||||
emitError(tr("Unable to parse device statistics: "), jsonError, reply, response);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case QNetworkReply::OperationCanceledError:
|
||||
return; // intended, not an error
|
||||
default:
|
||||
emit error(tr("Unable to request device statistics: ") + reply->errorString(), SyncthingErrorCategory::OverallConnection, reply->error());
|
||||
emitError(tr("Unable to request device statistics: "), SyncthingErrorCategory::OverallConnection, reply);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1202,7 +1210,7 @@ void SyncthingConnection::readDeviceStatistics()
|
|||
*/
|
||||
void SyncthingConnection::readErrors()
|
||||
{
|
||||
auto *reply = static_cast<QNetworkReply *>(sender());
|
||||
auto *const reply = static_cast<QNetworkReply *>(sender());
|
||||
reply->deleteLater();
|
||||
if (reply == m_errorsReply) {
|
||||
m_errorsReply = nullptr;
|
||||
|
@ -1215,8 +1223,9 @@ void SyncthingConnection::readErrors()
|
|||
|
||||
switch (reply->error()) {
|
||||
case QNetworkReply::NoError: {
|
||||
const QByteArray response(reply->readAll());
|
||||
QJsonParseError jsonError;
|
||||
const QJsonDocument replyDoc = QJsonDocument::fromJson(reply->readAll(), &jsonError);
|
||||
const QJsonDocument replyDoc = QJsonDocument::fromJson(response, &jsonError);
|
||||
if (jsonError.error == QJsonParseError::NoError) {
|
||||
for (const QJsonValue &errorVal : replyDoc.object().value(QStringLiteral("errors")).toArray()) {
|
||||
const QJsonObject errorObj(errorVal.toObject());
|
||||
|
@ -1231,7 +1240,7 @@ void SyncthingConnection::readErrors()
|
|||
}
|
||||
}
|
||||
} else {
|
||||
emit error(tr("Unable to parse errors: ") + jsonError.errorString(), SyncthingErrorCategory::Parsing, QNetworkReply::NoError);
|
||||
emitError(tr("Unable to parse errors: "), jsonError, reply, response);
|
||||
}
|
||||
|
||||
// since there seems no event for this data, keep polling
|
||||
|
@ -1243,7 +1252,7 @@ void SyncthingConnection::readErrors()
|
|||
case QNetworkReply::OperationCanceledError:
|
||||
return; // intended, not an error
|
||||
default:
|
||||
emit error(tr("Unable to request errors: ") + reply->errorString(), SyncthingErrorCategory::SpecificRequest, reply->error());
|
||||
emitError(tr("Unable to request errors: "), SyncthingErrorCategory::SpecificRequest, reply);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1252,14 +1261,14 @@ void SyncthingConnection::readErrors()
|
|||
*/
|
||||
void SyncthingConnection::readClearingErrors()
|
||||
{
|
||||
auto *reply = static_cast<QNetworkReply *>(sender());
|
||||
auto *const reply = static_cast<QNetworkReply *>(sender());
|
||||
reply->deleteLater();
|
||||
|
||||
switch (reply->error()) {
|
||||
case QNetworkReply::NoError:
|
||||
break;
|
||||
default:
|
||||
emit error(tr("Unable to request clearing errors: ") + reply->errorString(), SyncthingErrorCategory::SpecificRequest, reply->error());
|
||||
emitError(tr("Unable to request clearing errors: "), SyncthingErrorCategory::SpecificRequest, reply);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1268,7 +1277,7 @@ void SyncthingConnection::readClearingErrors()
|
|||
*/
|
||||
void SyncthingConnection::readEvents()
|
||||
{
|
||||
auto *reply = static_cast<QNetworkReply *>(sender());
|
||||
auto *const reply = static_cast<QNetworkReply *>(sender());
|
||||
reply->deleteLater();
|
||||
if (reply == m_eventsReply) {
|
||||
m_eventsReply = nullptr;
|
||||
|
@ -1276,8 +1285,9 @@ void SyncthingConnection::readEvents()
|
|||
|
||||
switch (reply->error()) {
|
||||
case QNetworkReply::NoError: {
|
||||
const QByteArray response(reply->readAll());
|
||||
QJsonParseError jsonError;
|
||||
const QJsonDocument replyDoc = QJsonDocument::fromJson(reply->readAll(), &jsonError);
|
||||
const QJsonDocument replyDoc = QJsonDocument::fromJson(response, &jsonError);
|
||||
if (jsonError.error == QJsonParseError::NoError) {
|
||||
const QJsonArray replyArray = replyDoc.array();
|
||||
emit newEvents(replyArray);
|
||||
|
@ -1312,7 +1322,7 @@ void SyncthingConnection::readEvents()
|
|||
}
|
||||
}
|
||||
} else {
|
||||
emit error(tr("Unable to parse Syncthing events: ") + jsonError.errorString(), SyncthingErrorCategory::Parsing, QNetworkReply::NoError);
|
||||
emitError(tr("Unable to parse Syncthing events: "), jsonError, reply, response);
|
||||
setStatus(SyncthingStatus::Disconnected);
|
||||
if (m_autoReconnectTimer.interval()) {
|
||||
m_autoReconnectTimer.start();
|
||||
|
@ -1335,7 +1345,7 @@ void SyncthingConnection::readEvents()
|
|||
}
|
||||
return;
|
||||
default:
|
||||
emit error(tr("Unable to request Syncthing events: ") + reply->errorString(), SyncthingErrorCategory::OverallConnection, reply->error());
|
||||
emitError(tr("Unable to request Syncthing events: "), SyncthingErrorCategory::OverallConnection, reply);
|
||||
setStatus(SyncthingStatus::Disconnected);
|
||||
if (m_autoReconnectTimer.interval()) {
|
||||
m_autoReconnectTimer.start();
|
||||
|
@ -1570,7 +1580,7 @@ void SyncthingConnection::readDeviceEvent(DateTime eventTime, const QString &eve
|
|||
|
||||
/*!
|
||||
* \brief Reads results of requestEvents().
|
||||
* \remarks TODO
|
||||
* \todo Implement this.
|
||||
*/
|
||||
void SyncthingConnection::readItemStarted(DateTime eventTime, const QJsonObject &eventData)
|
||||
{
|
||||
|
@ -1613,14 +1623,14 @@ void SyncthingConnection::readItemFinished(DateTime eventTime, const QJsonObject
|
|||
*/
|
||||
void SyncthingConnection::readRescan()
|
||||
{
|
||||
auto *reply = static_cast<QNetworkReply *>(sender());
|
||||
auto *const reply = static_cast<QNetworkReply *>(sender());
|
||||
reply->deleteLater();
|
||||
switch (reply->error()) {
|
||||
case QNetworkReply::NoError:
|
||||
emit rescanTriggered(reply->property("dirId").toString());
|
||||
break;
|
||||
default:
|
||||
emit error(tr("Unable to request rescan: ") + reply->errorString(), SyncthingErrorCategory::SpecificRequest, reply->error());
|
||||
emitError(tr("Unable to request rescan: "), SyncthingErrorCategory::SpecificRequest, reply);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1629,7 +1639,7 @@ void SyncthingConnection::readRescan()
|
|||
*/
|
||||
void SyncthingConnection::readDevPauseResume()
|
||||
{
|
||||
auto *reply = static_cast<QNetworkReply *>(sender());
|
||||
auto *const reply = static_cast<QNetworkReply *>(sender());
|
||||
reply->deleteLater();
|
||||
switch (reply->error()) {
|
||||
case QNetworkReply::NoError: {
|
||||
|
@ -1644,13 +1654,13 @@ void SyncthingConnection::readDevPauseResume()
|
|||
break;
|
||||
}
|
||||
default:
|
||||
emit error(tr("Unable to request device pause/resume: ") + reply->errorString(), SyncthingErrorCategory::SpecificRequest, reply->error());
|
||||
emitError(tr("Unable to request device pause/resume: "), SyncthingErrorCategory::SpecificRequest, reply);
|
||||
}
|
||||
}
|
||||
|
||||
void SyncthingConnection::readDirPauseResume()
|
||||
{
|
||||
auto *reply = static_cast<QNetworkReply *>(sender());
|
||||
auto *const reply = static_cast<QNetworkReply *>(sender());
|
||||
reply->deleteLater();
|
||||
switch (reply->error()) {
|
||||
case QNetworkReply::NoError: {
|
||||
|
@ -1665,7 +1675,7 @@ void SyncthingConnection::readDirPauseResume()
|
|||
break;
|
||||
}
|
||||
default:
|
||||
emit error(tr("Unable to request directory pause/resume: ") + reply->errorString(), SyncthingErrorCategory::SpecificRequest, reply->error());
|
||||
emitError(tr("Unable to request directory pause/resume: "), SyncthingErrorCategory::SpecificRequest, reply);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1674,14 +1684,14 @@ void SyncthingConnection::readDirPauseResume()
|
|||
*/
|
||||
void SyncthingConnection::readRestart()
|
||||
{
|
||||
auto *reply = static_cast<QNetworkReply *>(sender());
|
||||
auto *const reply = static_cast<QNetworkReply *>(sender());
|
||||
reply->deleteLater();
|
||||
switch (reply->error()) {
|
||||
case QNetworkReply::NoError:
|
||||
emit restartTriggered();
|
||||
break;
|
||||
default:
|
||||
emit error(tr("Unable to request restart: ") + reply->errorString(), SyncthingErrorCategory::SpecificRequest, reply->error());
|
||||
emitError(tr("Unable to request restart: "), SyncthingErrorCategory::SpecificRequest, reply);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1690,14 +1700,14 @@ void SyncthingConnection::readRestart()
|
|||
*/
|
||||
void SyncthingConnection::readShutdown()
|
||||
{
|
||||
auto *reply = static_cast<QNetworkReply *>(sender());
|
||||
auto *const reply = static_cast<QNetworkReply *>(sender());
|
||||
reply->deleteLater();
|
||||
switch (reply->error()) {
|
||||
case QNetworkReply::NoError:
|
||||
emit shutdownTriggered();
|
||||
break;
|
||||
default:
|
||||
emit error(tr("Unable to request shutdown: ") + reply->errorString(), SyncthingErrorCategory::SpecificRequest, reply->error());
|
||||
emitError(tr("Unable to request shutdown: "), SyncthingErrorCategory::SpecificRequest, reply);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1780,6 +1790,23 @@ void SyncthingConnection::emitNotification(DateTime when, const QString &message
|
|||
emit newNotification(when, message);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Internally called to emit a JSON parsing error.
|
||||
* \remarks Since in this case the reply has already been read, its response must be passed as extra argument.
|
||||
*/
|
||||
void SyncthingConnection::emitError(const QString &message, const QJsonParseError &jsonError, QNetworkReply *reply, const QByteArray &response)
|
||||
{
|
||||
emit error(message + jsonError.errorString(), SyncthingErrorCategory::Parsing, QNetworkReply::NoError, reply->request(), response);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Internally called to emit a network error (server replied error code are connection or server could not be reached at all).
|
||||
*/
|
||||
void SyncthingConnection::emitError(const QString &message, SyncthingErrorCategory category, QNetworkReply *reply)
|
||||
{
|
||||
emit error(message + reply->errorString(), category, reply->error(), reply->request(), reply->readAll());
|
||||
}
|
||||
|
||||
/*!
|
||||
* \fn SyncthingConnection::newConfig()
|
||||
* \brief Indicates new configuration (dirs, devs, ...) is available.
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include <QJsonObject>
|
||||
#include <QList>
|
||||
#include <QNetworkRequest>
|
||||
#include <QObject>
|
||||
#include <QSslError>
|
||||
#include <QTimer>
|
||||
|
@ -16,10 +17,10 @@
|
|||
|
||||
QT_FORWARD_DECLARE_CLASS(QNetworkAccessManager)
|
||||
QT_FORWARD_DECLARE_CLASS(QNetworkReply)
|
||||
QT_FORWARD_DECLARE_CLASS(QNetworkRequest)
|
||||
QT_FORWARD_DECLARE_CLASS(QUrlQuery)
|
||||
QT_FORWARD_DECLARE_CLASS(QJsonObject)
|
||||
QT_FORWARD_DECLARE_CLASS(QJsonArray)
|
||||
QT_FORWARD_DECLARE_CLASS(QJsonParseError)
|
||||
|
||||
class ConnectionTests;
|
||||
class MiscTests;
|
||||
|
@ -152,7 +153,8 @@ Q_SIGNALS:
|
|||
void devStatusChanged(const SyncthingDev &dev, int index);
|
||||
void downloadProgressChanged();
|
||||
void newNotification(ChronoUtilities::DateTime when, const QString &message);
|
||||
void error(const QString &errorMessage, SyncthingErrorCategory category, int networkError);
|
||||
void error(const QString &errorMessage, SyncthingErrorCategory category, int networkError, const QNetworkRequest &request = QNetworkRequest(),
|
||||
const QByteArray &response = QByteArray());
|
||||
void statusChanged(SyncthingStatus newStatus);
|
||||
void configDirChanged(const QString &newConfigDir);
|
||||
void myIdChanged(const QString &myNewId);
|
||||
|
@ -204,6 +206,8 @@ private Q_SLOTS:
|
|||
void autoReconnect();
|
||||
void setStatus(SyncthingStatus status);
|
||||
void emitNotification(ChronoUtilities::DateTime when, const QString &message);
|
||||
void emitError(const QString &message, const QJsonParseError &jsonError, QNetworkReply *reply, const QByteArray &response = QByteArray());
|
||||
void emitError(const QString &message, SyncthingErrorCategory category, QNetworkReply *reply);
|
||||
|
||||
private:
|
||||
QNetworkRequest prepareRequest(const QString &path, const QUrlQuery &query, bool rest = true);
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
#include "./trayicon.h"
|
||||
#include "./traywidget.h"
|
||||
|
||||
#include "../../widgets/misc/errorviewdialog.h"
|
||||
#include "../../widgets/misc/statusinfo.h"
|
||||
#include "../../widgets/misc/textviewdialog.h"
|
||||
#include "../../widgets/settings/settings.h"
|
||||
|
||||
#include "../../model/syncthingicons.h"
|
||||
|
@ -39,6 +41,7 @@ TrayIcon::TrayIcon(const QString &connectionConfig, QObject *parent)
|
|||
, m_initialized(false)
|
||||
, m_trayMenu(this, connectionConfig)
|
||||
, m_status(SyncthingStatus::Disconnected)
|
||||
, m_messageClickedAction(TrayIconMessageClickedAction::None)
|
||||
{
|
||||
// set context menu
|
||||
connect(m_contextMenu.addAction(QIcon::fromTheme(QStringLiteral("internet-web-browser"),
|
||||
|
@ -74,7 +77,7 @@ TrayIcon::TrayIcon(const QString &connectionConfig, QObject *parent)
|
|||
// connect signals and slots
|
||||
SyncthingConnection *connection = &(m_trayMenu.widget()->connection());
|
||||
connect(this, &TrayIcon::activated, this, &TrayIcon::handleActivated);
|
||||
connect(this, &TrayIcon::messageClicked, m_trayMenu.widget(), &TrayWidget::dismissNotifications);
|
||||
connect(this, &TrayIcon::messageClicked, this, &TrayIcon::handleMessageClicked);
|
||||
connect(connection, &SyncthingConnection::error, this, &TrayIcon::showInternalError);
|
||||
connect(connection, &SyncthingConnection::newNotification, this, &TrayIcon::showSyncthingNotification);
|
||||
connect(connection, &SyncthingConnection::statusChanged, this, &TrayIcon::handleConnectionStatusChanged);
|
||||
|
@ -83,6 +86,7 @@ TrayIcon::TrayIcon(const QString &connectionConfig, QObject *parent)
|
|||
static_cast<void (SyncthingConnection::*)(void)>(&SyncthingConnection::connect));
|
||||
connect(&m_dbusNotifier, &DBusStatusNotifier::dismissNotificationsRequested, m_trayMenu.widget(), &TrayWidget::dismissNotifications);
|
||||
connect(&m_dbusNotifier, &DBusStatusNotifier::showNotificationsRequested, m_trayMenu.widget(), &TrayWidget::showNotifications);
|
||||
connect(&m_dbusNotifier, &DBusStatusNotifier::errorDetailsRequested, &ErrorViewDialog::showInstance);
|
||||
#endif
|
||||
m_initialized = true;
|
||||
}
|
||||
|
@ -121,6 +125,20 @@ void TrayIcon::handleActivated(QSystemTrayIcon::ActivationReason reason)
|
|||
}
|
||||
}
|
||||
|
||||
void TrayIcon::handleMessageClicked()
|
||||
{
|
||||
switch (m_messageClickedAction) {
|
||||
case TrayIconMessageClickedAction::None:
|
||||
return;
|
||||
case TrayIconMessageClickedAction::DismissNotification:
|
||||
m_trayMenu.widget()->dismissNotifications();
|
||||
break;
|
||||
case TrayIconMessageClickedAction::ShowInternalErrors:
|
||||
ErrorViewDialog::instance()->show();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void TrayIcon::handleConnectionStatusChanged(SyncthingStatus status)
|
||||
{
|
||||
if (m_initialized && m_status == status) {
|
||||
|
@ -131,7 +149,8 @@ void TrayIcon::handleConnectionStatusChanged(SyncthingStatus status)
|
|||
m_status = status;
|
||||
}
|
||||
|
||||
void TrayIcon::showInternalError(const QString &errorMsg, SyncthingErrorCategory category, int networkError)
|
||||
void TrayIcon::showInternalError(
|
||||
const QString &errorMsg, SyncthingErrorCategory category, int networkError, const QNetworkRequest &request, const QByteArray &response)
|
||||
{
|
||||
const auto &settings = Settings::values();
|
||||
#ifdef LIB_SYNCTHING_CONNECTOR_SUPPORT_SYSTEMD
|
||||
|
@ -148,14 +167,17 @@ void TrayIcon::showInternalError(const QString &errorMsg, SyncthingErrorCategory
|
|||
&& !service.isActiveWithoutSleepFor(settings.ignoreInavailabilityAfterStart)))
|
||||
#endif
|
||||
) {
|
||||
InternalError error(errorMsg, request.url(), response);
|
||||
#ifdef QT_UTILITIES_SUPPORT_DBUS_NOTIFICATIONS
|
||||
if (settings.dbusNotifications) {
|
||||
m_dbusNotifier.showInternalError(errorMsg, category, networkError);
|
||||
m_dbusNotifier.showInternalError(error);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
m_messageClickedAction = TrayIconMessageClickedAction::ShowInternalErrors;
|
||||
showMessage(tr("Error"), errorMsg, QSystemTrayIcon::Critical);
|
||||
}
|
||||
ErrorViewDialog::addError(move(error));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -171,6 +193,7 @@ void TrayIcon::showSyncthingNotification(ChronoUtilities::DateTime when, const Q
|
|||
Q_UNUSED(when)
|
||||
#endif
|
||||
{
|
||||
m_messageClickedAction = TrayIconMessageClickedAction::DismissNotification;
|
||||
showMessage(tr("Syncthing notification - click to dismiss"), message, QSystemTrayIcon::Warning);
|
||||
}
|
||||
}
|
||||
|
@ -206,6 +229,7 @@ void TrayIcon::showStatusNotification(SyncthingStatus status)
|
|||
} else
|
||||
#endif
|
||||
{
|
||||
m_messageClickedAction = TrayIconMessageClickedAction::None;
|
||||
showMessage(QCoreApplication::applicationName(), tr("Disconnected from Syncthing"), QSystemTrayIcon::Warning);
|
||||
}
|
||||
}
|
||||
|
@ -241,6 +265,7 @@ void TrayIcon::showStatusNotification(SyncthingStatus status)
|
|||
} else
|
||||
#endif
|
||||
{
|
||||
m_messageClickedAction = TrayIconMessageClickedAction::None;
|
||||
showMessage(QCoreApplication::applicationName(), message, QSystemTrayIcon::Information);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <QSystemTrayIcon>
|
||||
|
||||
QT_FORWARD_DECLARE_CLASS(QPixmap)
|
||||
QT_FORWARD_DECLARE_CLASS(QNetworkRequest)
|
||||
|
||||
namespace Data {
|
||||
enum class SyncthingStatus;
|
||||
|
@ -19,6 +20,8 @@ enum class SyncthingErrorCategory;
|
|||
|
||||
namespace QtGui {
|
||||
|
||||
enum class TrayIconMessageClickedAction { None, DismissNotification, ShowInternalErrors };
|
||||
|
||||
class TrayIcon : public QSystemTrayIcon {
|
||||
Q_OBJECT
|
||||
|
||||
|
@ -27,13 +30,15 @@ public:
|
|||
TrayMenu &trayMenu();
|
||||
|
||||
public slots:
|
||||
void showInternalError(const QString &errorMsg, Data::SyncthingErrorCategory category, int networkError);
|
||||
void showInternalError(
|
||||
const QString &errorMsg, Data::SyncthingErrorCategory category, int networkError, const QNetworkRequest &request, const QByteArray &response);
|
||||
void showSyncthingNotification(ChronoUtilities::DateTime when, const QString &message);
|
||||
void showStatusNotification(Data::SyncthingStatus status);
|
||||
void updateStatusIconAndText();
|
||||
|
||||
private slots:
|
||||
void handleActivated(QSystemTrayIcon::ActivationReason reason);
|
||||
void handleMessageClicked();
|
||||
void handleConnectionStatusChanged(Data::SyncthingStatus status);
|
||||
|
||||
private:
|
||||
|
@ -44,6 +49,7 @@ private:
|
|||
#ifdef QT_UTILITIES_SUPPORT_DBUS_NOTIFICATIONS
|
||||
DBusStatusNotifier m_dbusNotifier;
|
||||
#endif
|
||||
TrayIconMessageClickedAction m_messageClickedAction;
|
||||
};
|
||||
|
||||
inline TrayMenu &TrayIcon::trayMenu()
|
||||
|
|
|
@ -17,8 +17,10 @@ set(WIDGETS_HEADER_FILES
|
|||
webview/webpage.h
|
||||
webview/webviewdialog.h
|
||||
misc/textviewdialog.h
|
||||
misc/errorviewdialog.h
|
||||
misc/statusinfo.h
|
||||
misc/dbusstatusnotifier.h
|
||||
misc/internalerror.h
|
||||
)
|
||||
set(WIDGETS_SRC_FILES
|
||||
settings/settings.cpp
|
||||
|
@ -26,6 +28,7 @@ set(WIDGETS_SRC_FILES
|
|||
webview/webpage.cpp
|
||||
webview/webviewdialog.cpp
|
||||
misc/textviewdialog.cpp
|
||||
misc/errorviewdialog.cpp
|
||||
misc/statusinfo.cpp
|
||||
misc/dbusstatusnotifier.cpp
|
||||
)
|
||||
|
|
|
@ -18,6 +18,8 @@ DBusStatusNotifier::DBusStatusNotifier(QObject *parent)
|
|||
m_disconnectedNotification.setMessage(tr("Disconnected from Syncthing"));
|
||||
m_disconnectedNotification.setActions(QStringList(tr("Try to reconnect")));
|
||||
connect(&m_disconnectedNotification, &DBusNotification::actionInvoked, this, &DBusStatusNotifier::connectRequested);
|
||||
m_internalErrorNotification.setActions(QStringList({ QStringLiteral("details"), tr("View details") }));
|
||||
connect(&m_internalErrorNotification, &DBusNotification::actionInvoked, this, &DBusStatusNotifier::errorDetailsRequested);
|
||||
m_syncthingNotification.setActions(QStringList({ QStringLiteral("show"), tr("Show"), QStringLiteral("dismiss"), tr("Dismiss") }));
|
||||
connect(&m_syncthingNotification, &DBusNotification::actionInvoked, this, &DBusStatusNotifier::handleSyncthingNotificationAction);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#if !defined(SYNCTHINGWIDGETS_DBUSSTATUSNOTIFIER_H) && defined(QT_UTILITIES_SUPPORT_DBUS_NOTIFICATIONS)
|
||||
#define SYNCTHINGWIDGETS_DBUSSTATUSNOTIFIER_H
|
||||
|
||||
#include "../global.h"
|
||||
#include "./internalerror.h"
|
||||
|
||||
#include <qtutilities/misc/dbusnotification.h>
|
||||
|
||||
|
@ -24,7 +24,7 @@ public:
|
|||
public Q_SLOTS:
|
||||
void showDisconnect();
|
||||
void hideDisconnect();
|
||||
void showInternalError(const QString &errorMsg, Data::SyncthingErrorCategory category, int networkError);
|
||||
void showInternalError(const InternalError &error);
|
||||
void showSyncthingNotification(ChronoUtilities::DateTime when, const QString &message);
|
||||
void showSyncComplete(const QString &message);
|
||||
|
||||
|
@ -32,6 +32,7 @@ Q_SIGNALS:
|
|||
void connectRequested();
|
||||
void dismissNotificationsRequested();
|
||||
void showNotificationsRequested();
|
||||
void errorDetailsRequested();
|
||||
|
||||
private Q_SLOTS:
|
||||
void handleSyncthingNotificationAction(const QString &action);
|
||||
|
@ -53,11 +54,9 @@ inline void DBusStatusNotifier::hideDisconnect()
|
|||
m_disconnectedNotification.hide();
|
||||
}
|
||||
|
||||
inline void DBusStatusNotifier::showInternalError(const QString &errorMsg, Data::SyncthingErrorCategory category, int networkError)
|
||||
inline void DBusStatusNotifier::showInternalError(const InternalError &error)
|
||||
{
|
||||
Q_UNUSED(category)
|
||||
Q_UNUSED(networkError)
|
||||
m_internalErrorNotification.update(errorMsg);
|
||||
m_internalErrorNotification.update(error.message);
|
||||
}
|
||||
|
||||
inline void DBusStatusNotifier::showSyncthingNotification(ChronoUtilities::DateTime when, const QString &message)
|
||||
|
|
|
@ -0,0 +1,100 @@
|
|||
#include "./errorviewdialog.h"
|
||||
|
||||
#include <QHBoxLayout>
|
||||
#include <QIcon>
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
#include <QStringBuilder>
|
||||
#include <QTextBrowser>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
|
||||
using namespace std;
|
||||
using namespace ChronoUtilities;
|
||||
using namespace Data;
|
||||
|
||||
namespace QtGui {
|
||||
|
||||
ErrorViewDialog *ErrorViewDialog::s_instance = nullptr;
|
||||
std::vector<InternalError> ErrorViewDialog::s_internalErrors;
|
||||
|
||||
ErrorViewDialog::ErrorViewDialog()
|
||||
: TextViewDialog(tr("Internal errors"))
|
||||
, m_request(tr("Request URL:"))
|
||||
, m_response(tr("Response:"))
|
||||
, m_statusLabel(new QLabel(this))
|
||||
{
|
||||
if (!s_instance) {
|
||||
s_instance = this;
|
||||
}
|
||||
|
||||
// add layout to show status and additional buttons
|
||||
auto *const buttonLayout = new QHBoxLayout(this);
|
||||
buttonLayout->setMargin(0);
|
||||
|
||||
// add label for overall status
|
||||
QFont boldFont(m_statusLabel->font());
|
||||
boldFont.setBold(true);
|
||||
m_statusLabel->setFont(boldFont);
|
||||
buttonLayout->addWidget(m_statusLabel);
|
||||
updateStatusLabel();
|
||||
|
||||
// add errors to text view
|
||||
for (const InternalError &error : s_internalErrors) {
|
||||
internalAddError(error);
|
||||
}
|
||||
|
||||
// add a button for clearing errors
|
||||
if (!s_internalErrors.empty()) {
|
||||
auto *const clearButton = new QPushButton(this);
|
||||
clearButton->setText(tr("Clear errors"));
|
||||
clearButton->setIcon(QIcon::fromTheme(QStringLiteral("edit-clear")));
|
||||
buttonLayout->setMargin(0);
|
||||
buttonLayout->addItem(new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum));
|
||||
buttonLayout->addWidget(clearButton);
|
||||
connect(clearButton, &QPushButton::clicked, &ErrorViewDialog::clearErrors);
|
||||
}
|
||||
|
||||
layout()->addItem(buttonLayout);
|
||||
}
|
||||
|
||||
ErrorViewDialog::~ErrorViewDialog()
|
||||
{
|
||||
if (s_instance == this) {
|
||||
s_instance = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void ErrorViewDialog::addError(InternalError &&newError)
|
||||
{
|
||||
s_internalErrors.emplace_back(newError);
|
||||
if (s_instance) {
|
||||
s_instance->internalAddError(s_internalErrors.back());
|
||||
s_instance->updateStatusLabel();
|
||||
}
|
||||
}
|
||||
|
||||
void ErrorViewDialog::internalAddError(const InternalError &error)
|
||||
{
|
||||
browser()->append(QString::fromUtf8(error.when.toString(DateTimeOutputFormat::DateAndTime, true).data()) % QChar(':') % QChar(' ') % error.message
|
||||
% QChar('\n') % m_request % QChar(' ') % error.url.toString(QUrl::FullyDecoded) % QChar('\n') % m_response % QChar('\n')
|
||||
% QString::fromLocal8Bit(error.response) % QChar('\n'));
|
||||
}
|
||||
|
||||
void ErrorViewDialog::updateStatusLabel()
|
||||
{
|
||||
m_statusLabel->setText(tr("%1 error(s) occured", nullptr, static_cast<int>(min<size_t>(s_internalErrors.size(), numeric_limits<int>::max())))
|
||||
.arg(s_internalErrors.size()));
|
||||
}
|
||||
|
||||
void ErrorViewDialog::clearErrors()
|
||||
{
|
||||
s_internalErrors.clear();
|
||||
if (s_instance) {
|
||||
s_instance->updateStatusLabel();
|
||||
s_instance->browser()->clear();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
#ifndef SYNCTHINGWIDGETS_ERRORVIEWDIALOG_H
|
||||
#define SYNCTHINGWIDGETS_ERRORVIEWDIALOG_H
|
||||
|
||||
#include "./internalerror.h"
|
||||
#include "./textviewdialog.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
QT_FORWARD_DECLARE_CLASS(QLabel)
|
||||
|
||||
namespace QtGui {
|
||||
|
||||
class SYNCTHINGWIDGETS_EXPORT ErrorViewDialog : public TextViewDialog {
|
||||
Q_OBJECT
|
||||
public:
|
||||
~ErrorViewDialog();
|
||||
static ErrorViewDialog *instance();
|
||||
static void addError(InternalError &&newError);
|
||||
|
||||
public Q_SLOTS:
|
||||
static void showInstance();
|
||||
static void clearErrors();
|
||||
|
||||
private Q_SLOTS:
|
||||
void internalAddError(const InternalError &error);
|
||||
void updateStatusLabel();
|
||||
|
||||
private:
|
||||
ErrorViewDialog();
|
||||
|
||||
const QString m_request;
|
||||
const QString m_response;
|
||||
QLabel *const m_statusLabel;
|
||||
static ErrorViewDialog *s_instance;
|
||||
static std::vector<InternalError> s_internalErrors;
|
||||
};
|
||||
|
||||
inline ErrorViewDialog *ErrorViewDialog::instance()
|
||||
{
|
||||
return s_instance ? s_instance : (s_instance = new ErrorViewDialog);
|
||||
}
|
||||
|
||||
inline void ErrorViewDialog::showInstance()
|
||||
{
|
||||
instance()->show();
|
||||
}
|
||||
}
|
||||
|
||||
#endif // SYNCTHINGWIDGETS_ERRORVIEWDIALOG_H
|
|
@ -0,0 +1,32 @@
|
|||
#ifndef SYNCTHINGWIDGETS_INTERNALERROR_H
|
||||
#define SYNCTHINGWIDGETS_INTERNALERROR_H
|
||||
|
||||
#include "../global.h"
|
||||
|
||||
#include <c++utilities/chrono/datetime.h>
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QString>
|
||||
#include <QUrl>
|
||||
|
||||
namespace QtGui {
|
||||
|
||||
struct SYNCTHINGWIDGETS_EXPORT InternalError {
|
||||
InternalError(const QString &message = QString(), const QUrl &url = QUrl(), const QByteArray &response = QByteArray());
|
||||
|
||||
QString message;
|
||||
QUrl url;
|
||||
QByteArray response;
|
||||
ChronoUtilities::DateTime when;
|
||||
};
|
||||
|
||||
inline InternalError::InternalError(const QString &message, const QUrl &url, const QByteArray &response)
|
||||
: message(message)
|
||||
, url(url)
|
||||
, response(response)
|
||||
, when(ChronoUtilities::DateTime::now())
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
#endif // SYNCTHINGWIDGETS_INTERNALERROR_H
|
|
@ -72,7 +72,7 @@ TextViewDialog *TextViewDialog::forDirectoryErrors(const Data::SyncthingDir &dir
|
|||
// add errors to text view and find errors about non-empty directories to be removed
|
||||
QStringList nonEmptyDirs;
|
||||
for (const SyncthingItemError &error : dir.itemErrors) {
|
||||
browser->append(error.path % QChar(':') % QChar(' ') % QChar('\n') % error.message % QChar('\n'));
|
||||
browser->append(error.path % QChar(':') % QChar('\n') % error.message % QChar('\n'));
|
||||
if (error.message.endsWith(QStringLiteral("directory not empty"))) {
|
||||
nonEmptyDirs << dir.path + error.path;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#ifndef TEXTVIEWDIALOG_H
|
||||
#define TEXTVIEWDIALOG_H
|
||||
#ifndef SYNCTHINGWIDGETS_TEXTVIEWDIALOG_H
|
||||
#define SYNCTHINGWIDGETS_TEXTVIEWDIALOG_H
|
||||
|
||||
#include "../global.h"
|
||||
|
||||
|
@ -21,7 +21,7 @@ public:
|
|||
QTextBrowser *browser();
|
||||
static TextViewDialog *forDirectoryErrors(const Data::SyncthingDir &dir);
|
||||
|
||||
signals:
|
||||
Q_SIGNALS:
|
||||
void reload();
|
||||
|
||||
protected:
|
||||
|
@ -37,4 +37,4 @@ inline QTextBrowser *TextViewDialog::browser()
|
|||
}
|
||||
}
|
||||
|
||||
#endif // TEXTVIEWDIALOG_H
|
||||
#endif // SYNCTHINGWIDGETS_TEXTVIEWDIALOG_H
|
||||
|
|
Loading…
Reference in New Issue