Improve sync complete notifications
* Also consider remote updates * Still massive notifications when uploading about local folder completed
This commit is contained in:
parent
e6642245df
commit
6828e96b53
|
@ -500,7 +500,7 @@ void Application::printDir(const RelevantDir &relevantDir) const
|
|||
printProperty("Remote progress", dir->areRemotesUpToDate() ? "all up-to-date" : "some need bytes");
|
||||
for (const auto &completionForDev : dir->completionByDevice) {
|
||||
printProperty(m_connection.deviceNameOrId(completionForDev.first).toLocal8Bit().data(),
|
||||
argsToString(dataSizeToString(completionForDev.second.globalBytes - completionForDev.second.neededBytes), ' ', '/', ' ',
|
||||
argsToString(dataSizeToString(completionForDev.second.globalBytes - completionForDev.second.needed.bytes), ' ', '/', ' ',
|
||||
dataSizeToString(completionForDev.second.globalBytes), ' ', '(', static_cast<int>(completionForDev.second.percentage), " %)")
|
||||
.data(),
|
||||
nullptr, 6);
|
||||
|
|
|
@ -1657,16 +1657,24 @@ void SyncthingConnection::readDirEvent(DateTime eventTime, const QString &eventT
|
|||
emit dirStatusChanged(*dirInfo, index);
|
||||
} else if (eventType == QLatin1String("FolderSummary")) {
|
||||
readDirSummary(eventTime, eventData.value(QStringLiteral("summary")).toObject(), *dirInfo, index);
|
||||
} else if (eventType == QLatin1String("FolderCompletion")) {
|
||||
const int percentage = jsonValueToInt<int>(eventData.value(QStringLiteral("completion")));
|
||||
dirInfo->globalStats.bytes = jsonValueToInt(eventData.value(QStringLiteral("globalBytes")), dirInfo->globalStats.bytes);
|
||||
dirInfo->neededStats.bytes = jsonValueToInt(eventData.value(QStringLiteral("needBytes")), dirInfo->neededStats.bytes);
|
||||
if (percentage >= 0 && percentage <= 100) {
|
||||
dirInfo->completionPercentage = percentage;
|
||||
emit dirStatusChanged(*dirInfo, index);
|
||||
if (percentage == 100) {
|
||||
emit dirCompleted(eventTime, *dirInfo, index);
|
||||
}
|
||||
} else if (eventType == QLatin1String("FolderCompletion") && dirInfo->lastStatisticsUpdate < eventTime) {
|
||||
auto &neededStats(dirInfo->neededStats);
|
||||
auto &globalStats(dirInfo->globalStats);
|
||||
// backup previous statistics -> if there's no difference after all, don't emit completed event
|
||||
const auto previouslyUpdated(!dirInfo->lastStatisticsUpdate.isNull());
|
||||
const auto previouslyNeeded(neededStats);
|
||||
const auto previouslyGlobal(globalStats);
|
||||
// read values from event data
|
||||
globalStats.bytes = jsonValueToInt(eventData.value(QStringLiteral("globalBytes")), globalStats.bytes);
|
||||
neededStats.bytes = jsonValueToInt(eventData.value(QStringLiteral("needBytes")), neededStats.bytes);
|
||||
neededStats.deletes = jsonValueToInt(eventData.value(QStringLiteral("needDeletes")), neededStats.deletes);
|
||||
neededStats.deletes = jsonValueToInt(eventData.value(QStringLiteral("needItems")), neededStats.files);
|
||||
dirInfo->lastStatisticsUpdate = eventTime;
|
||||
dirInfo->completionPercentage
|
||||
= globalStats.bytes ? static_cast<int>((globalStats.bytes - neededStats.bytes) * 100 / globalStats.bytes) : 100;
|
||||
emit dirStatusChanged(*dirInfo, index);
|
||||
if (neededStats.isNull() && previouslyUpdated && (neededStats != previouslyNeeded || globalStats != previouslyGlobal)) {
|
||||
emit dirCompleted(eventTime, *dirInfo, index);
|
||||
}
|
||||
} else if (eventType == QLatin1String("FolderScanProgress")) {
|
||||
const double current = eventData.value(QStringLiteral("current")).toDouble(0);
|
||||
|
@ -1963,20 +1971,25 @@ bool SyncthingConnection::readDirSummary(DateTime eventTime, const QJsonObject &
|
|||
return false;
|
||||
}
|
||||
|
||||
// update statistics
|
||||
// backup previous statistics -> if there's no difference after all, don't emit completed event
|
||||
auto &globalStats(dir.globalStats);
|
||||
auto &localStats(dir.localStats);
|
||||
auto &neededStats(dir.neededStats);
|
||||
const auto previouslyUpdated(!dir.lastStatisticsUpdate.isNull());
|
||||
const auto previouslyGlobal(globalStats);
|
||||
const auto previouslyNeeded(neededStats);
|
||||
|
||||
// update statistics
|
||||
globalStats.bytes = jsonValueToInt(summary.value(QStringLiteral("globalBytes")));
|
||||
globalStats.deletes = jsonValueToInt(summary.value(QStringLiteral("globalDeleted")));
|
||||
globalStats.files = jsonValueToInt(summary.value(QStringLiteral("globalFiles")));
|
||||
globalStats.dirs = jsonValueToInt(summary.value(QStringLiteral("globalDirectories")));
|
||||
globalStats.symlinks = jsonValueToInt(summary.value(QStringLiteral("globalSymlinks")));
|
||||
auto &localStats(dir.localStats);
|
||||
localStats.bytes = jsonValueToInt(summary.value(QStringLiteral("localBytes")));
|
||||
localStats.deletes = jsonValueToInt(summary.value(QStringLiteral("localDeleted")));
|
||||
localStats.files = jsonValueToInt(summary.value(QStringLiteral("localFiles")));
|
||||
localStats.dirs = jsonValueToInt(summary.value(QStringLiteral("localDirectories")));
|
||||
localStats.symlinks = jsonValueToInt(summary.value(QStringLiteral("localSymlinks")));
|
||||
auto &neededStats(dir.neededStats);
|
||||
neededStats.bytes = jsonValueToInt(summary.value(QStringLiteral("needBytes")));
|
||||
neededStats.deletes = jsonValueToInt(summary.value(QStringLiteral("needDeletes")));
|
||||
neededStats.files = jsonValueToInt(summary.value(QStringLiteral("needFiles")));
|
||||
|
@ -1997,7 +2010,12 @@ bool SyncthingConnection::readDirSummary(DateTime eventTime, const QJsonObject &
|
|||
}
|
||||
}
|
||||
|
||||
dir.completionPercentage = globalStats.bytes ? static_cast<int>((globalStats.bytes - neededStats.bytes) * 100 / globalStats.bytes) : 100;
|
||||
|
||||
emit dirStatusChanged(dir, index);
|
||||
if (neededStats.isNull() && previouslyUpdated && (neededStats != previouslyNeeded || globalStats != previouslyGlobal)) {
|
||||
emit dirCompleted(eventTime, dir, index);
|
||||
}
|
||||
return stateChanged;
|
||||
}
|
||||
|
||||
|
@ -2033,13 +2051,23 @@ void SyncthingConnection::readCompletion()
|
|||
// update the relevant completion info
|
||||
const auto replyObj(replyDoc.object());
|
||||
auto &completion = dir->completionByDevice[devId];
|
||||
auto &needed(completion.needed);
|
||||
const auto previouslyUpdated = !completion.lastUpdate.isNull();
|
||||
const auto previouslyNeeded = !needed.isNull();
|
||||
const auto previousGlobalBytes = completion.globalBytes;
|
||||
completion.lastUpdate = DateTime::gmtNow();
|
||||
completion.percentage = replyObj.value(QLatin1String("completion")).toDouble();
|
||||
completion.globalBytes = jsonValueToInt(replyObj.value(QLatin1String("globalBytes")));
|
||||
completion.neededBytes = jsonValueToInt(replyObj.value(QLatin1String("needBytes")));
|
||||
completion.neededItems = jsonValueToInt(replyObj.value(QLatin1String("needItems")));
|
||||
completion.neededDeletes = jsonValueToInt(replyObj.value(QLatin1String("needDeletes")));
|
||||
needed.bytes = jsonValueToInt(replyObj.value(QLatin1String("needBytes")), needed.bytes);
|
||||
needed.items = jsonValueToInt(replyObj.value(QLatin1String("needItems")), needed.items);
|
||||
needed.deletes = jsonValueToInt(replyObj.value(QLatin1String("needDeletes")), needed.deletes);
|
||||
emit dirStatusChanged(*dir, index);
|
||||
if (needed.isNull() && previouslyUpdated && (previouslyNeeded || previousGlobalBytes != completion.globalBytes)) {
|
||||
int devIndex;
|
||||
if (const auto *devInfo = findDevInfo(devId, devIndex)) {
|
||||
emit dirCompleted(DateTime::gmtNow(), *dir, index, devInfo);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
|
|
@ -19,6 +19,7 @@ struct LIB_SYNCTHING_CONNECTOR_EXPORT SyncthingDev {
|
|||
SyncthingDev(const QString &id = QString(), const QString &name = QString());
|
||||
QString statusString() const;
|
||||
bool isConnected() const;
|
||||
const QString displayName() const;
|
||||
|
||||
QString id;
|
||||
QString name;
|
||||
|
@ -56,6 +57,11 @@ inline bool SyncthingDev::isConnected() const
|
|||
}
|
||||
}
|
||||
|
||||
inline const QString SyncthingDev::displayName() const
|
||||
{
|
||||
return name.isEmpty() ? id : name;
|
||||
}
|
||||
|
||||
} // namespace Data
|
||||
|
||||
Q_DECLARE_METATYPE(Data::SyncthingDev)
|
||||
|
|
|
@ -135,7 +135,7 @@ QStringRef SyncthingDir::pathWithoutTrailingSlash() const
|
|||
bool SyncthingDir::areRemotesUpToDate() const
|
||||
{
|
||||
for (const auto &completionForDev : completionByDevice) {
|
||||
if (completionForDev.second.neededBytes) {
|
||||
if (!completionForDev.second.needed.isNull()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,11 +55,31 @@ struct LIB_SYNCTHING_CONNECTOR_EXPORT SyncthingCompletion {
|
|||
ChronoUtilities::DateTime lastUpdate;
|
||||
double percentage = 0;
|
||||
quint64 globalBytes = 0;
|
||||
quint64 neededBytes = 0;
|
||||
quint64 neededItems = 0;
|
||||
quint64 neededDeletes = 0;
|
||||
struct Needed {
|
||||
quint64 bytes = 0;
|
||||
quint64 items = 0;
|
||||
quint64 deletes = 0;
|
||||
constexpr bool isNull() const;
|
||||
constexpr bool operator==(const Needed &other) const;
|
||||
constexpr bool operator!=(const Needed &other) const;
|
||||
} needed;
|
||||
};
|
||||
|
||||
constexpr bool SyncthingCompletion::Needed::isNull() const
|
||||
{
|
||||
return bytes == 0 && items == 0 && deletes == 0;
|
||||
}
|
||||
|
||||
constexpr bool SyncthingCompletion::Needed::operator==(const SyncthingCompletion::Needed &other) const
|
||||
{
|
||||
return bytes == other.bytes && items == other.items && deletes == other.deletes;
|
||||
}
|
||||
|
||||
constexpr bool SyncthingCompletion::Needed::operator!=(const SyncthingCompletion::Needed &other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
struct LIB_SYNCTHING_CONNECTOR_EXPORT SyncthingStatistics {
|
||||
quint64 bytes = 0;
|
||||
quint64 deletes = 0;
|
||||
|
@ -68,6 +88,8 @@ struct LIB_SYNCTHING_CONNECTOR_EXPORT SyncthingStatistics {
|
|||
quint64 symlinks = 0;
|
||||
|
||||
constexpr bool isNull() const;
|
||||
constexpr bool operator==(const SyncthingStatistics &other) const;
|
||||
constexpr bool operator!=(const SyncthingStatistics &other) const;
|
||||
};
|
||||
|
||||
constexpr bool SyncthingStatistics::isNull() const
|
||||
|
@ -75,6 +97,16 @@ constexpr bool SyncthingStatistics::isNull() const
|
|||
return bytes == 0 && deletes == 0 && dirs == 0 && files == 0 && symlinks == 0;
|
||||
}
|
||||
|
||||
constexpr bool SyncthingStatistics::operator==(const SyncthingStatistics &other) const
|
||||
{
|
||||
return bytes == other.bytes && deletes == other.deletes && dirs == other.dirs && files == other.files && symlinks == other.symlinks;
|
||||
}
|
||||
|
||||
constexpr bool SyncthingStatistics::operator!=(const SyncthingStatistics &other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
struct LIB_SYNCTHING_CONNECTOR_EXPORT SyncthingDir {
|
||||
SyncthingDir(const QString &id = QString(), const QString &label = QString(), const QString &path = QString());
|
||||
bool assignStatus(const QString &statusStr, ChronoUtilities::DateTime time);
|
||||
|
|
|
@ -90,21 +90,16 @@ void SyncthingNotifier::emitConnectedAndDisconnected(SyncthingStatus newStatus)
|
|||
*/
|
||||
void SyncthingNotifier::emitSyncComplete(ChronoUtilities::DateTime when, const SyncthingDir &dir, int index, const SyncthingDev *remoteDev)
|
||||
{
|
||||
VAR_UNUSED(when)
|
||||
VAR_UNUSED(index)
|
||||
VAR_UNUSED(remoteDev)
|
||||
|
||||
// discard event if not enabled
|
||||
if ((m_enabledNotifications & SyncthingHighLevelNotification::SyncComplete) == 0 || !m_initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
// discard event if too old so we don't get "sync complete" messages for all dirs on startup
|
||||
if ((DateTime::gmtNow() - when) > TimeSpan::fromSeconds(5)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// format the notification message
|
||||
const auto message(syncCompleteString(std::vector<const SyncthingDir *>{ &dir }));
|
||||
const auto message(syncCompleteString(std::vector<const SyncthingDir *>{ &dir }, remoteDev));
|
||||
if (!message.isEmpty()) {
|
||||
emit syncComplete(message);
|
||||
}
|
||||
|
|
|
@ -64,21 +64,26 @@ QString directoryStatusString(const SyncthingStatistics &stats)
|
|||
/*!
|
||||
* \brief Returns the "sync complete" notication message for the specified directories.
|
||||
*/
|
||||
QString syncCompleteString(const std::vector<const SyncthingDir *> &completedDirs)
|
||||
QString syncCompleteString(const std::vector<const SyncthingDir *> &completedDirs, const SyncthingDev *remoteDevice)
|
||||
{
|
||||
const auto devName(remoteDevice ? remoteDevice->displayName() : QString());
|
||||
switch (completedDirs.size()) {
|
||||
case 0:
|
||||
return QString();
|
||||
case 1:
|
||||
return QCoreApplication::translate("Data::Utils", "Synchronization of %1 complete").arg(completedDirs.front()->displayName());
|
||||
if (devName.isEmpty()) {
|
||||
return QCoreApplication::translate("Data::Utils", "Synchronization of %1 complete").arg(completedDirs.front()->displayName());
|
||||
}
|
||||
return QCoreApplication::translate("Data::Utils", "Synchronization of %1 on %2 complete").arg(completedDirs.front()->displayName(), devName);
|
||||
default:;
|
||||
}
|
||||
QStringList names;
|
||||
names.reserve(static_cast<int>(completedDirs.size()));
|
||||
for (const auto *dir : completedDirs) {
|
||||
names << dir->displayName();
|
||||
const auto names(things(completedDirs, [](const auto *dir) { return dir->displayName(); }));
|
||||
if (devName.isEmpty()) {
|
||||
return QCoreApplication::translate("Data::Utils", "Synchronization of the following directories complete:\n")
|
||||
+ names.join(QStringLiteral(", "));
|
||||
}
|
||||
return QCoreApplication::translate("Data::Utils", "Synchronization of the following devices complete:\n") + names.join(QStringLiteral(", "));
|
||||
return QCoreApplication::translate("Data::Utils", "Synchronization of the following directories on %1 complete:\n").arg(devName)
|
||||
+ names.join(QStringLiteral(", "));
|
||||
}
|
||||
|
||||
/*!
|
||||
|
|
|
@ -23,11 +23,13 @@ namespace Data {
|
|||
|
||||
struct SyncthingStatistics;
|
||||
struct SyncthingDir;
|
||||
struct SyncthingDev;
|
||||
|
||||
QString LIB_SYNCTHING_CONNECTOR_EXPORT agoString(ChronoUtilities::DateTime dateTime);
|
||||
QString LIB_SYNCTHING_CONNECTOR_EXPORT trafficString(uint64 total, double rate);
|
||||
QString LIB_SYNCTHING_CONNECTOR_EXPORT directoryStatusString(const Data::SyncthingStatistics &stats);
|
||||
QString LIB_SYNCTHING_CONNECTOR_EXPORT syncCompleteString(const std::vector<const SyncthingDir *> &completedDirs);
|
||||
QString LIB_SYNCTHING_CONNECTOR_EXPORT syncCompleteString(
|
||||
const std::vector<const SyncthingDir *> &completedDirs, const SyncthingDev *remoteDevice = nullptr);
|
||||
bool LIB_SYNCTHING_CONNECTOR_EXPORT isLocal(const QUrl &url);
|
||||
bool LIB_SYNCTHING_CONNECTOR_EXPORT setDirectoriesPaused(QJsonObject &syncthingConfig, const QStringList &dirIds, bool paused);
|
||||
bool LIB_SYNCTHING_CONNECTOR_EXPORT setDevicesPaused(QJsonObject &syncthingConfig, const QStringList &dirs, bool paused);
|
||||
|
@ -43,14 +45,19 @@ constexpr int LIB_SYNCTHING_CONNECTOR_EXPORT trQuandity(quint64 quandity)
|
|||
return quandity > std::numeric_limits<int>::max() ? std::numeric_limits<int>::max() : static_cast<int>(quandity);
|
||||
}
|
||||
|
||||
template <class Objects, class Accessor> QStringList LIB_SYNCTHING_CONNECTOR_EXPORT things(const Objects &objects, Accessor accessor)
|
||||
{
|
||||
QStringList things;
|
||||
things.reserve(objects.size());
|
||||
for (const auto &object : objects) {
|
||||
things << accessor(object);
|
||||
}
|
||||
return things;
|
||||
}
|
||||
|
||||
template <class Objects> QStringList LIB_SYNCTHING_CONNECTOR_EXPORT ids(const Objects &objects)
|
||||
{
|
||||
QStringList ids;
|
||||
ids.reserve(objects.size());
|
||||
for (const auto &object : objects) {
|
||||
ids << object.id;
|
||||
}
|
||||
return ids;
|
||||
return things(objects, [](const auto &object) { return object.id; });
|
||||
}
|
||||
} // namespace Data
|
||||
|
||||
|
|
Loading…
Reference in New Issue