Prevent disconnect notification when stopping Syncthing manually

This commit is contained in:
Martchus 2017-01-10 23:46:16 +01:00
parent a66790af6c
commit ee59c2b1c4
7 changed files with 96 additions and 51 deletions

View File

@ -54,8 +54,8 @@ SyncthingConnection::SyncthingConnection(const QString &syncthingUrl, const QByt
m_keepPolling(false),
m_reconnecting(false),
m_lastEventId(0),
m_trafficPollInterval(2000),
m_devStatsPollInterval(60000),
m_trafficPollTimer(),
m_devStatsPollTimer(),
m_autoReconnectTimer(),
m_autoReconnectTries(0),
m_totalIncomingTraffic(0),
@ -72,6 +72,14 @@ SyncthingConnection::SyncthingConnection(const QString &syncthingUrl, const QByt
m_hasStatus(false),
m_lastFileDeleted(false)
{
m_trafficPollTimer.setInterval(2000);
m_trafficPollTimer.setTimerType(Qt::VeryCoarseTimer);
m_trafficPollTimer.setSingleShot(true);
QObject::connect(&m_trafficPollTimer, &QTimer::timeout, this, &SyncthingConnection::requestConnections);
m_devStatsPollTimer.setInterval(60000);
m_devStatsPollTimer.setTimerType(Qt::VeryCoarseTimer);
m_devStatsPollTimer.setSingleShot(true);
QObject::connect(&m_devStatsPollTimer, &QTimer::timeout, this, &SyncthingConnection::requestDeviceStatistics);
m_autoReconnectTimer.setTimerType(Qt::VeryCoarseTimer);
QObject::connect(&m_autoReconnectTimer, &QTimer::timeout, this, &SyncthingConnection::autoReconnect);
}
@ -129,7 +137,7 @@ void SyncthingConnection::connect()
if(!isConnected()) {
m_reconnecting = m_hasConfig = m_hasStatus = false;
if(m_apiKey.isEmpty() || m_syncthingUrl.isEmpty()) {
emit error(tr("Connection configuration is insufficient."), SyncthingErrorCategory::OverallConnection);
emit error(tr("Connection configuration is insufficient."), SyncthingErrorCategory::OverallConnection, QNetworkReply::NoError);
return;
}
requestConfig();
@ -216,7 +224,7 @@ void SyncthingConnection::continueReconnecting()
m_lastFileName.clear();
m_lastFileDeleted = false;
if(m_apiKey.isEmpty() || m_syncthingUrl.isEmpty()) {
emit error(tr("Connection configuration is insufficient."), SyncthingErrorCategory::OverallConnection);
emit error(tr("Connection configuration is insufficient."), SyncthingErrorCategory::OverallConnection, QNetworkReply::NoError);
return;
}
requestConfig();
@ -586,7 +594,7 @@ QMetaObject::Connection SyncthingConnection::requestQrCode(const QString &text,
callback(reply->readAll());
break;
default:
emit error(tr("Unable to request QR-Code: ") + reply->errorString(), SyncthingErrorCategory::SpecificRequest);
emit error(tr("Unable to request QR-Code: ") + reply->errorString(), SyncthingErrorCategory::SpecificRequest, reply->error());
}
});
}
@ -615,11 +623,11 @@ QMetaObject::Connection SyncthingConnection::requestLog(std::function<void (cons
}
callback(logEntries);
} else {
emit error(tr("Unable to parse Syncthing log: ") + jsonError.errorString(), SyncthingErrorCategory::Parsing);
emit error(tr("Unable to parse Syncthing log: ") + jsonError.errorString(), SyncthingErrorCategory::Parsing, QNetworkReply::NoError);
}
break;
} default:
emit error(tr("Unable to request Syncthing log: ") + reply->errorString(), SyncthingErrorCategory::SpecificRequest);
emit error(tr("Unable to request Syncthing log: ") + reply->errorString(), SyncthingErrorCategory::SpecificRequest, reply->error());
}
});
}
@ -653,13 +661,13 @@ bool SyncthingConnection::loadSelfSignedCertificate()
// find cert
const QString certPath = !m_configDir.isEmpty() ? (m_configDir + QStringLiteral("/https-cert.pem")) : SyncthingConfig::locateHttpsCertificate();
if(certPath.isEmpty()) {
emit error(tr("Unable to locate certificate used by Syncthing."), SyncthingErrorCategory::OverallConnection);
emit error(tr("Unable to locate certificate used by Syncthing."), SyncthingErrorCategory::OverallConnection, QNetworkReply::NoError);
return false;
}
// add exception
const QList<QSslCertificate> certs = QSslCertificate::fromPath(certPath);
if(certs.isEmpty()) {
emit error(tr("Unable to load certificate used by Syncthing."), SyncthingErrorCategory::OverallConnection);
emit error(tr("Unable to load certificate used by Syncthing."), SyncthingErrorCategory::OverallConnection, QNetworkReply::NoError);
return false;
}
const QSslCertificate &cert = certs.at(0);
@ -743,13 +751,13 @@ void SyncthingConnection::readConfig()
continueConnecting();
}
} else {
emit error(tr("Unable to parse Syncthing config: ") + jsonError.errorString(), SyncthingErrorCategory::Parsing);
emit error(tr("Unable to parse Syncthing config: ") + jsonError.errorString(), SyncthingErrorCategory::Parsing, QNetworkReply::NoError);
}
break;
} case QNetworkReply::OperationCanceledError:
return; // intended, not an error
default:
emit error(tr("Unable to request Syncthing config: ") + reply->errorString(), SyncthingErrorCategory::OverallConnection);
emit error(tr("Unable to request Syncthing config: ") + reply->errorString(), SyncthingErrorCategory::OverallConnection, reply->error());
setStatus(SyncthingStatus::Disconnected);
if(m_autoReconnectTimer.interval()) {
m_autoReconnectTimer.start();
@ -847,13 +855,13 @@ void SyncthingConnection::readStatus()
m_hasStatus = true;
continueConnecting();
} else {
emit error(tr("Unable to parse Syncthing status: ") + jsonError.errorString(), SyncthingErrorCategory::Parsing);
emit error(tr("Unable to parse Syncthing status: ") + jsonError.errorString(), SyncthingErrorCategory::Parsing, QNetworkReply::NoError);
}
break;
} case QNetworkReply::OperationCanceledError:
return; // intended, not an error
default:
emit error(tr("Unable to request Syncthing status: ") + reply->errorString(), SyncthingErrorCategory::OverallConnection);
emit error(tr("Unable to request Syncthing status: ") + reply->errorString(), SyncthingErrorCategory::OverallConnection, reply->error());
}
}
@ -924,17 +932,17 @@ void SyncthingConnection::readConnections()
m_lastConnectionsUpdate = DateTime::gmtNow();
// since there seems no event for this data, just request every 2 seconds
if(m_keepPolling) {
QTimer::singleShot(m_trafficPollInterval, Qt::VeryCoarseTimer, this, &SyncthingConnection::requestConnections);
if(m_keepPolling && m_trafficPollTimer.interval()) {
m_trafficPollTimer.start();
}
} else {
emit error(tr("Unable to parse connections: ") + jsonError.errorString(), SyncthingErrorCategory::Parsing);
emit error(tr("Unable to parse connections: ") + jsonError.errorString(), SyncthingErrorCategory::Parsing, QNetworkReply::NoError);
}
break;
} case QNetworkReply::OperationCanceledError:
return; // intended, not an error
default:
emit error(tr("Unable to request connections: ") + reply->errorString(), SyncthingErrorCategory::OverallConnection);
emit error(tr("Unable to request connections: ") + reply->errorString(), SyncthingErrorCategory::OverallConnection, reply->error());
}
}
@ -988,13 +996,13 @@ void SyncthingConnection::readDirStatistics()
++index;
}
} else {
emit error(tr("Unable to parse directory statistics: ") + jsonError.errorString(), SyncthingErrorCategory::Parsing);
emit error(tr("Unable to parse directory statistics: ") + jsonError.errorString(), SyncthingErrorCategory::Parsing, QNetworkReply::NoError);
}
break;
} case QNetworkReply::OperationCanceledError:
return; // intended, not an error
default:
emit error(tr("Unable to request directory statistics: ") + reply->errorString(), SyncthingErrorCategory::OverallConnection);
emit error(tr("Unable to request directory statistics: ") + reply->errorString(), SyncthingErrorCategory::OverallConnection, reply->error());
}
}
@ -1026,17 +1034,17 @@ void SyncthingConnection::readDeviceStatistics()
++index;
}
// since there seems no event for this data, just request every minute
if(m_keepPolling) {
QTimer::singleShot(m_devStatsPollInterval, Qt::VeryCoarseTimer, this, &SyncthingConnection::requestDeviceStatistics);
if(m_keepPolling && m_devStatsPollTimer.interval()) {
m_devStatsPollTimer.start();
}
} else {
emit error(tr("Unable to parse device statistics: ") + jsonError.errorString(), SyncthingErrorCategory::Parsing);
emit error(tr("Unable to parse device statistics: ") + jsonError.errorString(), SyncthingErrorCategory::Parsing, QNetworkReply::NoError);
}
break;
} case QNetworkReply::OperationCanceledError:
return; // intended, not an error
default:
emit error(tr("Unable to request device statistics: ") + reply->errorString(), SyncthingErrorCategory::OverallConnection);
emit error(tr("Unable to request device statistics: ") + reply->errorString(), SyncthingErrorCategory::OverallConnection, reply->error());
}
}
@ -1074,7 +1082,7 @@ void SyncthingConnection::readErrors()
}
}
} else {
emit error(tr("Unable to parse errors: ") + jsonError.errorString(), SyncthingErrorCategory::Parsing);
emit error(tr("Unable to parse errors: ") + jsonError.errorString(), SyncthingErrorCategory::Parsing, QNetworkReply::NoError);
}
// since there seems no event for this data, just request every thirty seconds, FIXME: make interval configurable
@ -1085,7 +1093,7 @@ void SyncthingConnection::readErrors()
} case QNetworkReply::OperationCanceledError:
return; // intended, not an error
default:
emit error(tr("Unable to request errors: ") + reply->errorString(), SyncthingErrorCategory::OverallConnection);
emit error(tr("Unable to request errors: ") + reply->errorString(), SyncthingErrorCategory::OverallConnection, reply->error());
}
}
@ -1138,7 +1146,7 @@ void SyncthingConnection::readEvents()
}
}
} else {
emit error(tr("Unable to parse Syncthing events: ") + jsonError.errorString(), SyncthingErrorCategory::Parsing);
emit error(tr("Unable to parse Syncthing events: ") + jsonError.errorString(), SyncthingErrorCategory::Parsing, QNetworkReply::NoError);
setStatus(SyncthingStatus::Disconnected);
if(m_autoReconnectTimer.interval()) {
m_autoReconnectTimer.start();
@ -1160,7 +1168,7 @@ void SyncthingConnection::readEvents()
}
return;
default:
emit error(tr("Unable to request Syncthing events: ") + reply->errorString(), SyncthingErrorCategory::OverallConnection);
emit error(tr("Unable to request Syncthing events: ") + reply->errorString(), SyncthingErrorCategory::OverallConnection, reply->error());
setStatus(SyncthingStatus::Disconnected);
if(m_autoReconnectTimer.interval()) {
m_autoReconnectTimer.start();
@ -1423,7 +1431,7 @@ void SyncthingConnection::readRescan()
emit rescanTriggered(reply->property("dirId").toString());
break;
default:
emit error(tr("Unable to request rescan: ") + reply->errorString(), SyncthingErrorCategory::SpecificRequest);
emit error(tr("Unable to request rescan: ") + reply->errorString(), SyncthingErrorCategory::SpecificRequest, reply->error());
}
}
@ -1443,7 +1451,7 @@ void SyncthingConnection::readPauseResume()
}
break;
default:
emit error(tr("Unable to request pause/resume: ") + reply->errorString(), SyncthingErrorCategory::SpecificRequest);
emit error(tr("Unable to request pause/resume: ") + reply->errorString(), SyncthingErrorCategory::SpecificRequest, reply->error());
}
}
@ -1459,7 +1467,7 @@ void SyncthingConnection::readRestart()
emit restartTriggered();
break;
default:
emit error(tr("Unable to request restart: ") + reply->errorString(), SyncthingErrorCategory::SpecificRequest);
emit error(tr("Unable to request restart: ") + reply->errorString(), SyncthingErrorCategory::SpecificRequest, reply->error());
}
}
@ -1475,7 +1483,7 @@ void SyncthingConnection::readShutdown()
emit shutdownTriggered();
break;
default:
emit error(tr("Unable to request shutdown: ") + reply->errorString(), SyncthingErrorCategory::SpecificRequest);
emit error(tr("Unable to request shutdown: ") + reply->errorString(), SyncthingErrorCategory::SpecificRequest, reply->error());
}
}
@ -1493,6 +1501,8 @@ void SyncthingConnection::setStatus(SyncthingStatus status)
case SyncthingStatus::Disconnected:
case SyncthingStatus::Reconnecting:
// don't consider synchronization finished in this this case
m_devStatsPollTimer.stop();
m_trafficPollTimer.stop();
m_syncedDirs.clear();
break;
default:

View File

@ -137,7 +137,7 @@ 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);
void error(const QString &errorMessage, SyncthingErrorCategory category, int networkError);
void statusChanged(SyncthingStatus newStatus);
void configDirChanged(const QString &newConfigDir);
void myIdChanged(const QString &myNewId);
@ -200,8 +200,8 @@ private:
bool m_keepPolling;
bool m_reconnecting;
int m_lastEventId;
int m_trafficPollInterval;
int m_devStatsPollInterval;
QTimer m_trafficPollTimer;
QTimer m_devStatsPollTimer;
QTimer m_autoReconnectTimer;
unsigned int m_autoReconnectTries;
QString m_configDir;
@ -325,7 +325,7 @@ inline void SyncthingConnection::considerAllNotificationsRead()
*/
inline int SyncthingConnection::trafficPollInterval() const
{
return m_trafficPollInterval;
return m_trafficPollTimer.interval();
}
/*!
@ -334,7 +334,10 @@ inline int SyncthingConnection::trafficPollInterval() const
*/
inline void SyncthingConnection::setTrafficPollInterval(int trafficPollInterval)
{
m_trafficPollInterval = trafficPollInterval;
if(!trafficPollInterval) {
m_trafficPollTimer.stop();
}
m_trafficPollTimer.setInterval(trafficPollInterval);
}
/*!
@ -343,7 +346,7 @@ inline void SyncthingConnection::setTrafficPollInterval(int trafficPollInterval)
*/
inline int SyncthingConnection::devStatsPollInterval() const
{
return m_devStatsPollInterval;
return m_devStatsPollTimer.interval();
}
/*!
@ -352,7 +355,10 @@ inline int SyncthingConnection::devStatsPollInterval() const
*/
inline void SyncthingConnection::setDevStatsPollInterval(int devStatsPollInterval)
{
m_devStatsPollInterval = devStatsPollInterval;
if(!devStatsPollInterval) {
m_devStatsPollTimer.stop();
}
m_devStatsPollTimer.setInterval(devStatsPollInterval);
}
/*!

View File

@ -42,7 +42,8 @@ SyncthingService::SyncthingService(QObject *parent) :
QObject(parent),
m_unit(nullptr),
m_service(nullptr),
m_properties(nullptr)
m_properties(nullptr),
m_manuallyStopped(false)
{
if(!s_manager) {
// register custom data types
@ -92,6 +93,7 @@ bool SyncthingService::isUnitAvailable() const
void SyncthingService::setRunning(bool running)
{
m_manuallyStopped = !running;
if(running) {
registerErrorHandler(s_manager->StartUnit(m_unitName, QStringLiteral("replace")), QT_TR_NOOP_UTF8("start unit"));
} else {
@ -138,18 +140,22 @@ void SyncthingService::handleUnitGet(QDBusPendingCallWatcher *watcher)
void SyncthingService::handlePropertiesChanged(const QString &interface, const QVariantMap &changedProperties, const QStringList &invalidatedProperties)
{
if(interface == m_unit->interface()) {
const bool running = isRunning();
const bool wasRunningBefore = isRunning();
if(handlePropertyChanged(m_activeState, &SyncthingService::activeStateChanged, QStringLiteral("ActiveState"), changedProperties, invalidatedProperties)
| handlePropertyChanged(m_subState, &SyncthingService::subStateChanged, QStringLiteral("SubState"), changedProperties, invalidatedProperties)) {
emit stateChanged(m_activeState, m_subState);
}
if(running != isRunning()) {
emit runningChanged(isRunning());
const bool currentlyRunning = isRunning();
if(currentlyRunning) {
m_manuallyStopped = false;
}
if(wasRunningBefore != currentlyRunning) {
emit runningChanged(currentlyRunning);
}
const bool enabled = isEnabled();
const bool wasEnabledBefore = isEnabled();
handlePropertyChanged(m_unitFileState, &SyncthingService::unitFileStateChanged, QStringLiteral("UnitFileState"), changedProperties, invalidatedProperties);
if(enabled != isEnabled()) {
if(wasEnabledBefore != isEnabled()) {
emit enabledChanged(isEnabled());
}

View File

@ -40,6 +40,7 @@ class SyncthingService : public QObject
Q_PROPERTY(QString description READ description NOTIFY descriptionChanged)
Q_PROPERTY(bool running READ isRunning WRITE setRunning NOTIFY runningChanged)
Q_PROPERTY(bool enable READ isEnabled WRITE setEnabled NOTIFY enabledChanged)
Q_PROPERTY(bool manuallyStopped READ isManuallyStopped)
public:
explicit SyncthingService(QObject *parent = nullptr);
@ -53,6 +54,7 @@ public:
const QString &description() const;
bool isRunning() const;
bool isEnabled() const;
bool isManuallyStopped() const;
public Q_SLOTS:
void setUnitName(const QString &unitName);
@ -99,6 +101,7 @@ private:
QString m_activeState;
QString m_subState;
QString m_unitFileState;
bool m_manuallyStopped;
};
inline const QString &SyncthingService::unitName() const
@ -151,6 +154,11 @@ inline bool SyncthingService::isEnabled() const
return m_unitFileState == QLatin1String("enabled");
}
inline bool SyncthingService::isManuallyStopped() const
{
return m_manuallyStopped;
}
inline void SyncthingService::enable()
{
setEnabled(true);

View File

@ -4,6 +4,9 @@
#include "../application/settings.h"
#include "../../connector/syncthingconnection.h"
#ifdef LIB_SYNCTHING_CONNECTOR_SUPPORT_SYSTEMD
# include "../../connector/syncthingservice.h"
#endif
#include <qtutilities/misc/dialogutils.h>
@ -11,6 +14,9 @@
#include <QSvgRenderer>
#include <QPainter>
#include <QPixmap>
#ifdef LIB_SYNCTHING_CONNECTOR_SUPPORT_SYSTEMD
# include <QNetworkReply>
#endif
using namespace std;
using namespace Dialogs;
@ -125,11 +131,15 @@ void TrayIcon::handleSyncthingNotificationAction(const QString &action)
}
}
void TrayIcon::showInternalError(const QString &errorMsg, SyncthingErrorCategory category)
void TrayIcon::showInternalError(const QString &errorMsg, SyncthingErrorCategory category, int networkError)
{
const auto &settings = Settings::values();
if(settings.notifyOn.internalErrors
&& (m_trayMenu.widget()->connection().autoReconnectTries() < 1 || category != SyncthingErrorCategory::OverallConnection)) {
&& (m_trayMenu.widget()->connection().autoReconnectTries() < 1 || category != SyncthingErrorCategory::OverallConnection)
#ifdef LIB_SYNCTHING_CONNECTOR_SUPPORT_SYSTEMD
&& (networkError != QNetworkReply::RemoteHostClosedError || !syncthingService().isManuallyStopped())
#endif
) {
#ifdef QT_UTILITIES_SUPPORT_DBUS_NOTIFICATIONS
if(settings.dbusNotifications) {
m_internalErrorNotification.update(errorMsg);
@ -175,7 +185,11 @@ void TrayIcon::updateStatusIconAndText(SyncthingStatus status)
} else {
setToolTip(tr("Not connected to Syncthing"));
}
if(m_initialized && settings.notifyOn.disconnect) {
if(m_initialized && settings.notifyOn.disconnect
#ifdef LIB_SYNCTHING_CONNECTOR_SUPPORT_SYSTEMD
&& !syncthingService().isManuallyStopped()
#endif
) {
#ifdef QT_UTILITIES_SUPPORT_DBUS_NOTIFICATIONS
if(settings.dbusNotifications) {
m_disconnectedNotification.show();

View File

@ -30,7 +30,7 @@ public:
TrayMenu &trayMenu();
public slots:
void showInternalError(const QString &errorMsg, Data::SyncthingErrorCategory category);
void showInternalError(const QString &errorMsg, Data::SyncthingErrorCategory category, int networkError);
void showSyncthingNotification(ChronoUtilities::DateTime when, const QString &message);
void updateStatusIconAndText(Data::SyncthingStatus status);

View File

@ -36,7 +36,7 @@
#include <functional>
#include <algorithm>
#include <iostream>
using namespace ApplicationUtilities;
using namespace ConversionUtilities;
using namespace ChronoUtilities;
@ -521,10 +521,11 @@ void TrayWidget::handleSystemdStatusChanged()
if(isRunning && m_selectedConnection) {
// auto-reconnect might have been disabled when unit was inactive before, so re-enable it according current connection settings
m_connection.setAutoReconnectInterval(m_selectedConnection->reconnectInterval);
// and reconnect in 5 seconds (Syncthing needs a few seconds till the API becomes available)
QTimer::singleShot(5000, Qt::VeryCoarseTimer, this, &TrayWidget::connectIfServiceRunning);
// and reconnect in 8 seconds (Syncthing needs a few seconds till the API becomes available)
QTimer::singleShot(8000, Qt::VeryCoarseTimer, this, &TrayWidget::connectIfServiceRunning);
} else {
// disable auto-reconnect if unit isn't running
std::cout << "disabling reconnect" << std::endl;
m_connection.setAutoReconnectInterval(0);
}
}