Refactor overall status compution

* Allow configuring the information to consider for computing the overall
  status via SyncthingStatusComputionFlags
    * Add flag to allow considering the status of remote devices for
      https://github.com/Martchus/syncthingtray/issues/74
    * Show only plain "idle" status when no flags are present for
      https://github.com/Martchus/syncthingtray/issues/76
* Set the default flags to keep the default behavior as-is
This commit is contained in:
Martchus 2020-12-31 02:48:18 +01:00
parent 392eb70b12
commit da911c6350
8 changed files with 143 additions and 30 deletions

View File

@ -579,20 +579,25 @@ void Application::printStatus(const ArgumentOccurrence &)
const auto &overallStats(m_connection.computeOverallDirStatistics()); const auto &overallStats(m_connection.computeOverallDirStatistics());
const auto *statusString = "idle"; const auto *statusString = "idle";
const auto *statusColor = "32"; const auto *statusColor = "32";
switch (m_connection.status()) { if (m_connection.hasOutOfSyncDirs()) {
case SyncthingStatus::Synchronizing:
statusString = "synchronizing";
statusColor = "34";
break;
case SyncthingStatus::Scanning:
statusString = "scanning";
statusColor = "34";
break;
case SyncthingStatus::OutOfSync:
statusString = "out-of-sync"; statusString = "out-of-sync";
statusColor = "31"; statusColor = "31";
break; } else {
default:; switch (m_connection.status()) {
case SyncthingStatus::Synchronizing:
statusString = "synchronizing";
statusColor = "34";
break;
case SyncthingStatus::RemoteNotInSync:
statusString = "remote synchronizing";
statusColor = "34";
break;
case SyncthingStatus::Scanning:
statusString = "scanning";
statusColor = "34";
break;
default:;
}
} }
if (!EscapeCodes::enabled) { if (!EscapeCodes::enabled) {
printProperty("Status", statusString); printProperty("Status", statusString);

View File

@ -72,6 +72,7 @@ SyncthingConnection::SyncthingConnection(const QString &syncthingUrl, const QByt
, m_syncthingUrl(syncthingUrl) , m_syncthingUrl(syncthingUrl)
, m_apiKey(apiKey) , m_apiKey(apiKey)
, m_status(SyncthingStatus::Disconnected) , m_status(SyncthingStatus::Disconnected)
, m_statusComputionFlags(SyncthingStatusComputionFlags::Default)
, m_keepPolling(false) , m_keepPolling(false)
, m_abortingAllRequests(false) , m_abortingAllRequests(false)
, m_abortingToReconnect(false) , m_abortingToReconnect(false)
@ -169,6 +170,8 @@ QString SyncthingConnection::statusText(SyncthingStatus status)
return tr("connected, paused"); return tr("connected, paused");
case SyncthingStatus::Synchronizing: case SyncthingStatus::Synchronizing:
return tr("connected, synchronizing"); return tr("connected, synchronizing");
case SyncthingStatus::RemoteNotInSync:
return tr("connected, remote not in sync");
default: default:
return tr("unknown"); return tr("unknown");
} }
@ -773,15 +776,29 @@ bool SyncthingConnection::applySettings(SyncthingConnectionSettings &connectionS
setDevStatsPollInterval(connectionSettings.devStatsPollInterval); setDevStatsPollInterval(connectionSettings.devStatsPollInterval);
setErrorsPollInterval(connectionSettings.errorsPollInterval); setErrorsPollInterval(connectionSettings.errorsPollInterval);
setAutoReconnectInterval(connectionSettings.reconnectInterval); setAutoReconnectInterval(connectionSettings.reconnectInterval);
setStatusComputionFlags(connectionSettings.statusComputionFlags);
return reconnectRequired; return reconnectRequired;
} }
/*! /*!
* \brief Sets the connection status. Ensures statusChanged() is emitted. * \brief Sets the connection status. Ensures statusChanged() is emitted if the status has actually changed.
* \param status Specifies the status; should be either SyncthingStatus::Disconnected, SyncthingStatus::Reconnecting, or * \param status Specifies the status; should be either SyncthingStatus::Disconnected, SyncthingStatus::Reconnecting, or
* SyncthingStatus::Idle. There is no use in specifying other values such as SyncthingStatus::Synchronizing as * SyncthingStatus::Idle. There is no use in specifying other values such as SyncthingStatus::Synchronizing as
* these are determined automatically within the method. * these are determined automatically within the method according to SyncthingConnection::statusComputionFlags().
*
* The precedence of the "connected" states from highest to lowest is:
* 1. SyncthingStatus::Synchronizing
* 2. SyncthingStatus::RemoteSynchronizing
* 3. SyncthingStatus::Scanning
* 4. SyncthingStatus::Paused
* 5. SyncthingStatus::Idle
*
* \remarks
* - The "out-of-sync" status is (currently) *not* handled by this function. One needs to query this via
* the SyncthingConnection::hasOutOfSyncDirs() function.
* - Whether notifications are available is *not* handled by this function. One needs to query this via
* SyncthingConnection::hasUnreadNotifications().
*/ */
void SyncthingConnection::setStatus(SyncthingStatus status) void SyncthingConnection::setStatus(SyncthingStatus status)
{ {
@ -802,30 +819,51 @@ void SyncthingConnection::setStatus(SyncthingStatus status)
// reset reconnect tries // reset reconnect tries
m_autoReconnectTries = 0; m_autoReconnectTries = 0;
// skip if no further status information should be gathered
if (m_statusComputionFlags == SyncthingStatusComputionFlags::None) {
status = SyncthingStatus::Idle;
break;
}
// check whether at least one directory is scanning, preparing to synchronize or synchronizing // check whether at least one directory is scanning, preparing to synchronize or synchronizing
// note: We don't distinguish between "preparing to sync" and "synchronizing" for computing the overall // note: We don't distinguish between "preparing to sync" and "synchronizing" for computing the overall
// status at the moment. // status at the moment.
bool scanning = false; auto scanning = false, synchronizing = false, remoteSynchronizing = false;
bool synchronizing = false; if (m_statusComputionFlags & SyncthingStatusComputionFlags::Synchronizing
for (const SyncthingDir &dir : m_dirs) { || m_statusComputionFlags & SyncthingStatusComputionFlags::Scanning) {
switch (dir.status) { for (const SyncthingDir &dir : m_dirs) {
case SyncthingDirStatus::PreparingToSync: switch (dir.status) {
case SyncthingDirStatus::Synchronizing: case SyncthingDirStatus::WaitingToSync:
synchronizing = true; case SyncthingDirStatus::PreparingToSync:
break; case SyncthingDirStatus::Synchronizing:
case SyncthingDirStatus::WaitingToScan: synchronizing = m_statusComputionFlags & SyncthingStatusComputionFlags::Synchronizing;
case SyncthingDirStatus::Scanning: break;
scanning = true; case SyncthingDirStatus::WaitingToScan:
break; case SyncthingDirStatus::Scanning:
default:; scanning = m_statusComputionFlags & SyncthingStatusComputionFlags::Scanning;
break;
default:;
}
if (synchronizing) {
break; // skip remaining dirs, "synchronizing" overrides "scanning" anyways
}
} }
if (synchronizing) { }
break; // skip remaining dirs, "synchronizing" overrides "scanning" anyways
// set the status to "remote synchronizing" if at least one remote device is still in progress
if (!synchronizing && (m_statusComputionFlags & SyncthingStatusComputionFlags::RemoteSynchronizing)) {
for (const SyncthingDev &dev : m_devs) {
if (dev.status == SyncthingDevStatus::Synchronizing) {
remoteSynchronizing = true;
break;
}
} }
} }
if (synchronizing) { if (synchronizing) {
status = SyncthingStatus::Synchronizing; status = SyncthingStatus::Synchronizing;
} else if (remoteSynchronizing) {
status = SyncthingStatus::RemoteNotInSync;
} else if (scanning) { } else if (scanning) {
status = SyncthingStatus::Scanning; status = SyncthingStatus::Scanning;
} else { } else {

View File

@ -30,6 +30,7 @@ class MiscTests;
namespace Data { namespace Data {
struct SyncthingConnectionSettings; struct SyncthingConnectionSettings;
enum class SyncthingStatusComputionFlags : quint64;
LIB_SYNCTHING_CONNECTOR_EXPORT QNetworkAccessManager &networkAccessManager(); LIB_SYNCTHING_CONNECTOR_EXPORT QNetworkAccessManager &networkAccessManager();
@ -54,6 +55,7 @@ class LIB_SYNCTHING_CONNECTOR_EXPORT SyncthingConnection : public QObject {
Q_PROPERTY(QString user READ user) Q_PROPERTY(QString user READ user)
Q_PROPERTY(QString password READ password) Q_PROPERTY(QString password READ password)
Q_PROPERTY(Data::SyncthingStatus status READ status NOTIFY statusChanged) Q_PROPERTY(Data::SyncthingStatus status READ status NOTIFY statusChanged)
Q_PROPERTY(Data::SyncthingStatusComputionFlags statusComputionFlags READ statusComputionFlags WRITE setStatusComputionFlags)
Q_PROPERTY(QString statusText READ statusText NOTIFY statusChanged) Q_PROPERTY(QString statusText READ statusText NOTIFY statusChanged)
Q_PROPERTY(bool connected READ isConnected NOTIFY statusChanged) Q_PROPERTY(bool connected READ isConnected NOTIFY statusChanged)
Q_PROPERTY(bool hasUnreadNotifications READ hasUnreadNotifications) Q_PROPERTY(bool hasUnreadNotifications READ hasUnreadNotifications)
@ -98,6 +100,8 @@ public:
SyncthingStatus status() const; SyncthingStatus status() const;
QString statusText() const; QString statusText() const;
static QString statusText(SyncthingStatus status); static QString statusText(SyncthingStatus status);
SyncthingStatusComputionFlags statusComputionFlags() const;
void setStatusComputionFlags(SyncthingStatusComputionFlags flags);
bool isConnected() const; bool isConnected() const;
bool hasPendingRequests() const; bool hasPendingRequests() const;
bool hasPendingRequestsIncludingEvents() const; bool hasPendingRequestsIncludingEvents() const;
@ -313,6 +317,8 @@ private:
QString m_user; QString m_user;
QString m_password; QString m_password;
SyncthingStatus m_status; SyncthingStatus m_status;
SyncthingStatusComputionFlags m_statusComputionFlags;
bool m_keepPolling; bool m_keepPolling;
bool m_abortingAllRequests; bool m_abortingAllRequests;
bool m_abortingToReconnect; bool m_abortingToReconnect;
@ -618,6 +624,25 @@ inline void SyncthingConnection::setRecordFileChanges(bool recordFileChanges)
m_recordFileChanges = recordFileChanges; m_recordFileChanges = recordFileChanges;
} }
/*!
* \brief Returns what information is considered to compute the overall status returned by status().
*/
inline SyncthingStatusComputionFlags SyncthingConnection::statusComputionFlags() const
{
return m_statusComputionFlags;
}
/*!
* \brief Sets what information should be used to compute the overall status returned by status().
*/
inline void SyncthingConnection::setStatusComputionFlags(SyncthingStatusComputionFlags flags)
{
if (m_statusComputionFlags != flags) {
m_statusComputionFlags = flags;
recalculateStatus();
}
}
/*! /*!
* \brief Returns the Syncthing home/configuration directory. * \brief Returns the Syncthing home/configuration directory.
*/ */

View File

@ -3,6 +3,8 @@
#include "./global.h" #include "./global.h"
#include <c++utilities/misc/flagenumclass.h>
#include <QByteArray> #include <QByteArray>
#include <QList> #include <QList>
#include <QSslError> #include <QSslError>
@ -10,6 +12,21 @@
namespace Data { namespace Data {
/*!
* \brief The SyncthingStatusComputionFlags enum specifies what information is considered to compute the overall state.
* \remarks The enum is supposed to be used as flag-enum.
*/
enum class SyncthingStatusComputionFlags : quint64 {
None = 0, /**< no further information is considered leaving SyncthingStatus::Disconnected, SyncthingStatus::Reconnecting,
SyncthingStatus::BeingDestroyed and SyncthingStatus::Idle the only possible states */
Scanning = (1 << 0), /**< the status SyncthingStatus::Scanning might be set (in addition) */
Synchronizing = (1 << 1), /**< the status SyncthingStatus::Synchronizing might be set (in addition) */
RemoteSynchronizing = (1 << 2), /**< the status SyncthingStatus::RemoteNotInSync might be set (in addition) */
DevicePaused = (1 << 3), /**< the status SyncthingStatus::Paused might be set if there's at least one paused device (in addition) */
Default = SyncthingStatusComputionFlags::Scanning | SyncthingStatusComputionFlags::Synchronizing | SyncthingStatusComputionFlags::DevicePaused,
/**< the default flags used all over the place */
};
struct LIB_SYNCTHING_CONNECTOR_EXPORT SyncthingConnectionSettings { struct LIB_SYNCTHING_CONNECTOR_EXPORT SyncthingConnectionSettings {
QString label; QString label;
QString syncthingUrl; QString syncthingUrl;
@ -23,6 +40,7 @@ struct LIB_SYNCTHING_CONNECTOR_EXPORT SyncthingConnectionSettings {
int reconnectInterval = defaultReconnectInterval; int reconnectInterval = defaultReconnectInterval;
QString httpsCertPath; QString httpsCertPath;
QList<QSslError> expectedSslErrors; QList<QSslError> expectedSslErrors;
SyncthingStatusComputionFlags statusComputionFlags = SyncthingStatusComputionFlags::Default;
bool autoConnect = false; bool autoConnect = false;
bool loadHttpsCert(); bool loadHttpsCert();
@ -33,4 +51,6 @@ struct LIB_SYNCTHING_CONNECTOR_EXPORT SyncthingConnectionSettings {
}; };
} // namespace Data } // namespace Data
CPP_UTILITIES_MARK_FLAG_ENUM_CLASS(Data, Data::SyncthingStatusComputionFlags)
#endif // SYNCTHINGCONNECTIONSETTINGS_H #endif // SYNCTHINGCONNECTIONSETTINGS_H

View File

@ -12,7 +12,25 @@ Q_NAMESPACE
extern LIB_SYNCTHING_CONNECTOR_EXPORT const QMetaObject staticMetaObject; extern LIB_SYNCTHING_CONNECTOR_EXPORT const QMetaObject staticMetaObject;
QT_ANNOTATE_CLASS(qt_qnamespace, "") /*end*/ QT_ANNOTATE_CLASS(qt_qnamespace, "") /*end*/
enum class SyncthingStatus { Disconnected, Reconnecting, Idle, Scanning, Paused, Synchronizing, OutOfSync, BeingDestroyed }; /*!
* \brief The SyncthingStatus enum specifies the overall status of the connection to Syncthing via its REST-API.
*
* Scanning, paused and (remote) synchronizing are only computed if the SyncthingStatusComputionFlags are set for
* these.
*
* This is *not* a flag enum even though the "connected" states are not exclusive. That's because only one icon can be
* shown at the same time anyways. Checkout SyncthingConnection::setStatus() for the precedence.
*/
enum class SyncthingStatus {
Disconnected = 0, /**< disconnected, possibly currently connecting */
Reconnecting = 1, /**< disconnected, currently re-connnecting */
BeingDestroyed = 7, /**< status is unknown; the SyncthingConnnection object is being destroyed anyways */
Idle = 2, /**< connected, no special status information available/determined */
Scanning = 3, /**< connected, at least one directory is scanning */
Paused = 4, /**< connected, at least one device is paused */
Synchronizing = 5, /**< connected, at least one local directory is waiting to sync, preparing to sync or synchronizing */
RemoteNotInSync = 8, /**< connected, at least one directory of a connected remote device is not in sync (still synchronizing, error, …) */
};
#if (QT_VERSION >= QT_VERSION_CHECK(5, 8, 0)) #if (QT_VERSION >= QT_VERSION_CHECK(5, 8, 0))
Q_ENUM_NS(SyncthingStatus) Q_ENUM_NS(SyncthingStatus)
#endif #endif

View File

@ -21,6 +21,7 @@ SyncthingStatusSelectionModel::SyncthingStatusSelectionModel(QObject *parent)
itemFor(SyncthingStatus::Scanning), itemFor(SyncthingStatus::Scanning),
itemFor(SyncthingStatus::Paused), itemFor(SyncthingStatus::Paused),
itemFor(SyncthingStatus::Synchronizing), itemFor(SyncthingStatus::Synchronizing),
itemFor(SyncthingStatus::RemoteNotInSync),
}); });
} }

View File

@ -389,6 +389,7 @@ void TrayWidget::handleStatusChanged(SyncthingStatus status)
case SyncthingStatus::Idle: case SyncthingStatus::Idle:
case SyncthingStatus::Scanning: case SyncthingStatus::Scanning:
case SyncthingStatus::Synchronizing: case SyncthingStatus::Synchronizing:
case SyncthingStatus::RemoteNotInSync:
m_ui->statusPushButton->setText(tr("Pause")); m_ui->statusPushButton->setText(tr("Pause"));
m_ui->statusPushButton->setToolTip(tr("Syncthing is running, click to pause all devices")); m_ui->statusPushButton->setToolTip(tr("Syncthing is running, click to pause all devices"));
m_ui->statusPushButton->setIcon(QIcon::fromTheme( m_ui->statusPushButton->setIcon(QIcon::fromTheme(
@ -609,6 +610,7 @@ void TrayWidget::changeStatus()
case SyncthingStatus::Idle: case SyncthingStatus::Idle:
case SyncthingStatus::Scanning: case SyncthingStatus::Scanning:
case SyncthingStatus::Synchronizing: case SyncthingStatus::Synchronizing:
case SyncthingStatus::RemoteNotInSync:
m_connection.pauseAllDevs(); m_connection.pauseAllDevs();
break; break;
case SyncthingStatus::Paused: case SyncthingStatus::Paused:

View File

@ -85,6 +85,10 @@ void StatusInfo::updateConnectionStatus(const SyncthingConnection &connection, c
m_statusText = QCoreApplication::translate("QtGui::StatusInfo", "Synchronization is ongoing"); m_statusText = QCoreApplication::translate("QtGui::StatusInfo", "Synchronization is ongoing");
m_statusIcon = &icons.sync; m_statusIcon = &icons.sync;
break; break;
case SyncthingStatus::RemoteNotInSync:
m_statusText = QCoreApplication::translate("QtGui::StatusInfo", "At least one remote directory is not in sync");
m_statusIcon = &icons.sync;
break;
default: default:
m_statusText = QCoreApplication::translate("QtGui::StatusInfo", "Status is unknown"); m_statusText = QCoreApplication::translate("QtGui::StatusInfo", "Status is unknown");
m_statusIcon = &icons.disconnected; m_statusIcon = &icons.disconnected;