mirror of
https://github.com/nextcloud/desktop.git
synced 2024-10-24 13:25:52 +03:00
SyncEngine & UI: Progress notifications for update phase
For each directory (local and remote, we have UI update throtting code) a signal is emitted. It is used by the settings dialog and the tray menu.
This commit is contained in:
parent
b40b670639
commit
ce2741cebc
14 changed files with 88 additions and 26 deletions
|
@ -134,23 +134,6 @@ enum csync_ftw_type_e {
|
||||||
CSYNC_FTW_TYPE_SKIP
|
CSYNC_FTW_TYPE_SKIP
|
||||||
};
|
};
|
||||||
|
|
||||||
enum csync_notify_type_e {
|
|
||||||
CSYNC_NOTIFY_INVALID,
|
|
||||||
CSYNC_NOTIFY_START_SYNC_SEQUENCE,
|
|
||||||
CSYNC_NOTIFY_START_DOWNLOAD,
|
|
||||||
CSYNC_NOTIFY_START_UPLOAD,
|
|
||||||
CSYNC_NOTIFY_PROGRESS,
|
|
||||||
CSYNC_NOTIFY_FINISHED_DOWNLOAD,
|
|
||||||
CSYNC_NOTIFY_FINISHED_UPLOAD,
|
|
||||||
CSYNC_NOTIFY_FINISHED_SYNC_SEQUENCE,
|
|
||||||
CSYNC_NOTIFY_START_DELETE,
|
|
||||||
CSYNC_NOTIFY_END_DELETE,
|
|
||||||
CSYNC_NOTIFY_ERROR,
|
|
||||||
CSYNC_NOTIFY_START_LOCAL_UPDATE,
|
|
||||||
CSYNC_NOTIFY_FINISHED_LOCAL_UPDATE,
|
|
||||||
CSYNC_NOTIFY_START_REMOTE_UPDATE,
|
|
||||||
CSYNC_NOTIFY_FINISHED_REMOTE_UPDATE
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CSync File Traversal structure.
|
* CSync File Traversal structure.
|
||||||
|
@ -203,6 +186,9 @@ typedef void (*csync_log_callback) (int verbosity,
|
||||||
const char *buffer,
|
const char *buffer,
|
||||||
void *userdata);
|
void *userdata);
|
||||||
|
|
||||||
|
typedef void (*csync_update_callback) (bool local,
|
||||||
|
const char *dirUrl,
|
||||||
|
void *userdata);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Allocate a csync context.
|
* @brief Allocate a csync context.
|
||||||
|
|
|
@ -24,6 +24,8 @@
|
||||||
|
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
|
|
||||||
|
#include "csync_private.h"
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* helper method to build up a user text for SSL problems, called from the
|
* helper method to build up a user text for SSL problems, called from the
|
||||||
|
@ -537,6 +539,8 @@ static struct listdir_context *fetch_resource_list(csync_owncloud_ctx_t *ctx, co
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ctx->csync_ctx->callbacks.update_callback(false, curi, ctx->csync_ctx->callbacks.update_callback_userdata);
|
||||||
|
|
||||||
fetchCtx = c_malloc( sizeof( struct listdir_context ));
|
fetchCtx = c_malloc( sizeof( struct listdir_context ));
|
||||||
if (!fetchCtx) {
|
if (!fetchCtx) {
|
||||||
errno = ENOMEM;
|
errno = ENOMEM;
|
||||||
|
|
|
@ -54,6 +54,7 @@ struct listdir_context *get_listdir_context_from_recursive_cache(csync_owncloud_
|
||||||
DEBUG_WEBDAV("get_listdir_context_from_recursive_cache No element %s in cache found", curi);
|
DEBUG_WEBDAV("get_listdir_context_from_recursive_cache No element %s in cache found", curi);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
ctx->csync_ctx->callbacks.update_callback(false, curi, ctx->csync_ctx->callbacks.update_callback_userdata);
|
||||||
|
|
||||||
/* Out of the element, create a listdir_context.. if we could be sure that it is immutable, we could ref instead.. need to investigate */
|
/* Out of the element, create a listdir_context.. if we could be sure that it is immutable, we could ref instead.. need to investigate */
|
||||||
fetchCtx = c_malloc( sizeof( struct listdir_context ));
|
fetchCtx = c_malloc( sizeof( struct listdir_context ));
|
||||||
|
@ -141,6 +142,12 @@ static void propfind_results_recursive_callback(void *userdata,
|
||||||
element->parent = NULL;
|
element->parent = NULL;
|
||||||
c_rbtree_insert(ctx->propfind_recursive_cache, element);
|
c_rbtree_insert(ctx->propfind_recursive_cache, element);
|
||||||
/* DEBUG_WEBDAV("results_recursive Added collection %s", newres->uri); */
|
/* DEBUG_WEBDAV("results_recursive Added collection %s", newres->uri); */
|
||||||
|
|
||||||
|
// We do this here and in get_listdir_context_from_recursive_cache because
|
||||||
|
// a recursive PROPFIND might take some time but we still want to
|
||||||
|
// be informed. Later when get_listdir_context_from_recursive_cache is
|
||||||
|
// called the DB queries might be the problem causing slowness, so do it again there then.
|
||||||
|
ctx->csync_ctx->callbacks.update_callback(false, path, ctx->csync_ctx->callbacks.update_callback_userdata);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -189,6 +196,7 @@ void fetch_resource_list_recursive(csync_owncloud_ctx_t *ctx, const char *uri, c
|
||||||
int depth = NE_DEPTH_INFINITE;
|
int depth = NE_DEPTH_INFINITE;
|
||||||
|
|
||||||
DEBUG_WEBDAV("fetch_resource_list_recursive Starting recursive propfind %s %s", uri, curi);
|
DEBUG_WEBDAV("fetch_resource_list_recursive Starting recursive propfind %s %s", uri, curi);
|
||||||
|
ctx->csync_ctx->callbacks.update_callback(false, curi, ctx->csync_ctx->callbacks.update_callback_userdata);
|
||||||
|
|
||||||
/* do a propfind request and parse the results in the results function, set as callback */
|
/* do a propfind request and parse the results in the results function, set as callback */
|
||||||
hdl = ne_propfind_create(ctx->dav_session.ctx, curi, depth);
|
hdl = ne_propfind_create(ctx->dav_session.ctx, curi, depth);
|
||||||
|
|
|
@ -87,6 +87,8 @@ struct csync_s {
|
||||||
struct {
|
struct {
|
||||||
csync_auth_callback auth_function;
|
csync_auth_callback auth_function;
|
||||||
void *userdata;
|
void *userdata;
|
||||||
|
csync_update_callback update_callback;
|
||||||
|
void *update_callback_userdata;
|
||||||
} callbacks;
|
} callbacks;
|
||||||
c_strlist_t *excludes;
|
c_strlist_t *excludes;
|
||||||
|
|
||||||
|
|
|
@ -48,6 +48,7 @@ csync_vio_handle_t *csync_vio_opendir(CSYNC *ctx, const char *name) {
|
||||||
return owncloud_opendir(ctx, name);
|
return owncloud_opendir(ctx, name);
|
||||||
break;
|
break;
|
||||||
case LOCAL_REPLICA:
|
case LOCAL_REPLICA:
|
||||||
|
ctx->callbacks.update_callback(ctx->replica, name, ctx->callbacks.update_callback_userdata);
|
||||||
return csync_vio_local_opendir(name);
|
return csync_vio_local_opendir(name);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -573,6 +573,14 @@ void AccountSettings::slotSetProgress(const QString& folder, const Progress::Inf
|
||||||
QStandardItem *item = itemForFolder( folder );
|
QStandardItem *item = itemForFolder( folder );
|
||||||
if( !item ) return;
|
if( !item ) return;
|
||||||
|
|
||||||
|
// switch on extra space.
|
||||||
|
item->setData( QVariant(true), FolderStatusDelegate::AddProgressSpace );
|
||||||
|
|
||||||
|
if (!progress._currentDiscoveredFolder.isEmpty()) {
|
||||||
|
item->setData( tr("Discovering %1").arg(progress._currentDiscoveredFolder) , FolderStatusDelegate::SyncProgressItemString );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if(!progress._lastCompletedItem.isEmpty()
|
if(!progress._lastCompletedItem.isEmpty()
|
||||||
&& Progress::isWarningKind(progress._lastCompletedItem._status)) {
|
&& Progress::isWarningKind(progress._lastCompletedItem._status)) {
|
||||||
int warnCount = item->data(FolderStatusDelegate::WarningCount).toInt();
|
int warnCount = item->data(FolderStatusDelegate::WarningCount).toInt();
|
||||||
|
@ -600,8 +608,7 @@ void AccountSettings::slotSetProgress(const QString& folder, const Progress::Inf
|
||||||
QString itemFileName = shortenFilename(folder, curItem._file);
|
QString itemFileName = shortenFilename(folder, curItem._file);
|
||||||
QString kindString = Progress::asActionString(curItem);
|
QString kindString = Progress::asActionString(curItem);
|
||||||
|
|
||||||
// switch on extra space.
|
|
||||||
item->setData( QVariant(true), FolderStatusDelegate::AddProgressSpace );
|
|
||||||
|
|
||||||
QString fileProgressString;
|
QString fileProgressString;
|
||||||
if (Progress::isSizeDependent(curItem._instruction)) {
|
if (Progress::isSizeDependent(curItem._instruction)) {
|
||||||
|
|
|
@ -591,6 +591,7 @@ void Folder::startSync(const QStringList &pathList)
|
||||||
//direct connection so the message box is blocking the sync.
|
//direct connection so the message box is blocking the sync.
|
||||||
connect(_engine.data(), SIGNAL(aboutToRemoveAllFiles(SyncFileItem::Direction,bool*)),
|
connect(_engine.data(), SIGNAL(aboutToRemoveAllFiles(SyncFileItem::Direction,bool*)),
|
||||||
SLOT(slotAboutToRemoveAllFiles(SyncFileItem::Direction,bool*)));
|
SLOT(slotAboutToRemoveAllFiles(SyncFileItem::Direction,bool*)));
|
||||||
|
connect(_engine.data(), SIGNAL(folderDiscovered(bool,QString)), this, SLOT(slotFolderDiscovered(bool,QString)));
|
||||||
connect(_engine.data(), SIGNAL(transmissionProgress(Progress::Info)), this, SLOT(slotTransmissionProgress(Progress::Info)));
|
connect(_engine.data(), SIGNAL(transmissionProgress(Progress::Info)), this, SLOT(slotTransmissionProgress(Progress::Info)));
|
||||||
connect(_engine.data(), SIGNAL(jobCompleted(SyncFileItem)), this, SLOT(slotJobCompleted(SyncFileItem)));
|
connect(_engine.data(), SIGNAL(jobCompleted(SyncFileItem)), this, SLOT(slotJobCompleted(SyncFileItem)));
|
||||||
|
|
||||||
|
@ -688,6 +689,13 @@ void Folder::slotEmitFinishedDelayed()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Folder::slotFolderDiscovered(bool local, QString folderName)
|
||||||
|
{
|
||||||
|
Progress::Info pi;
|
||||||
|
pi._currentDiscoveredFolder = folderName;
|
||||||
|
ProgressDispatcher::instance()->setProgressInfo(alias(), pi);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// the progress comes without a folder and the valid path set. Add that here
|
// the progress comes without a folder and the valid path set. Add that here
|
||||||
// and hand the result over to the progress dispatcher.
|
// and hand the result over to the progress dispatcher.
|
||||||
|
|
|
@ -157,6 +157,7 @@ private slots:
|
||||||
void slotCsyncUnavailable();
|
void slotCsyncUnavailable();
|
||||||
void slotSyncFinished();
|
void slotSyncFinished();
|
||||||
|
|
||||||
|
void slotFolderDiscovered(bool local, QString folderName);
|
||||||
void slotTransmissionProgress(const Progress::Info& pi);
|
void slotTransmissionProgress(const Progress::Info& pi);
|
||||||
void slotJobCompleted(const SyncFileItem&);
|
void slotJobCompleted(const SyncFileItem&);
|
||||||
|
|
||||||
|
|
|
@ -246,7 +246,7 @@ void FolderStatusDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
|
||||||
h += aliasMargin;
|
h += aliasMargin;
|
||||||
|
|
||||||
// Sync File Progress Bar: Show it if syncFile is not empty.
|
// Sync File Progress Bar: Show it if syncFile is not empty.
|
||||||
if( !overallString.isEmpty()) {
|
if( !overallString.isEmpty() || !itemString.isEmpty()) {
|
||||||
int fileNameTextHeight = subFm.boundingRect(tr("File")).height();
|
int fileNameTextHeight = subFm.boundingRect(tr("File")).height();
|
||||||
int barHeight = qMax(fileNameTextHeight, aliasFm.height()+4); ;
|
int barHeight = qMax(fileNameTextHeight, aliasFm.height()+4); ;
|
||||||
int overallWidth = option.rect.width()-2*aliasMargin;
|
int overallWidth = option.rect.width()-2*aliasMargin;
|
||||||
|
|
|
@ -444,13 +444,16 @@ void ownCloudGui::slotUpdateProgress(const QString &folder, const Progress::Info
|
||||||
{
|
{
|
||||||
Q_UNUSED(folder);
|
Q_UNUSED(folder);
|
||||||
|
|
||||||
QString totalSizeStr = Utility::octetsToString( progress._totalSize );
|
if (!progress._currentDiscoveredFolder.isEmpty()) {
|
||||||
if(progress._totalSize == 0 ) {
|
_actionStatus->setText( tr("Discovering %1")
|
||||||
|
.arg( progress._currentDiscoveredFolder ));
|
||||||
|
} else if (progress._totalSize == 0 ) {
|
||||||
quint64 currentFile = progress._completedFileCount + progress._currentItems.count();
|
quint64 currentFile = progress._completedFileCount + progress._currentItems.count();
|
||||||
_actionStatus->setText( tr("Syncing %1 of %2 (%3 left)")
|
_actionStatus->setText( tr("Syncing %1 of %2 (%3 left)")
|
||||||
.arg( currentFile ).arg( progress._totalFileCount )
|
.arg( currentFile ).arg( progress._totalFileCount )
|
||||||
.arg( Utility::timeToDescriptiveString(progress.totalEstimate().getEtaEstimate(), 2, " ",true) ) );
|
.arg( Utility::timeToDescriptiveString(progress.totalEstimate().getEtaEstimate(), 2, " ",true) ) );
|
||||||
} else {
|
} else {
|
||||||
|
QString totalSizeStr = Utility::octetsToString( progress._totalSize );
|
||||||
_actionStatus->setText( tr("Syncing %1 (%2 left)")
|
_actionStatus->setText( tr("Syncing %1 (%2 left)")
|
||||||
.arg( totalSizeStr )
|
.arg( totalSizeStr )
|
||||||
.arg( Utility::timeToDescriptiveString(progress.totalEstimate().getEtaEstimate(), 2, " ",true) ) );
|
.arg( Utility::timeToDescriptiveString(progress.totalEstimate().getEtaEstimate(), 2, " ",true) ) );
|
||||||
|
@ -491,7 +494,8 @@ void ownCloudGui::slotUpdateProgress(const QString &folder, const Progress::Info
|
||||||
slotRebuildRecentMenus();
|
slotRebuildRecentMenus();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (progress._completedFileCount == progress._totalFileCount) {
|
if (progress._completedFileCount == progress._totalFileCount
|
||||||
|
&& progress._currentDiscoveredFolder.isEmpty()) {
|
||||||
QTimer::singleShot(2000, this, SLOT(slotDisplayIdle()));
|
QTimer::singleShot(2000, this, SLOT(slotDisplayIdle()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -108,9 +108,11 @@ ProgressDispatcher::~ProgressDispatcher()
|
||||||
|
|
||||||
void ProgressDispatcher::setProgressInfo(const QString& folder, const Progress::Info& progress)
|
void ProgressDispatcher::setProgressInfo(const QString& folder, const Progress::Info& progress)
|
||||||
{
|
{
|
||||||
if( folder.isEmpty() ||
|
if( folder.isEmpty())
|
||||||
(progress._currentItems.size() == 0
|
// The update phase now also has progress
|
||||||
&& progress._totalFileCount == 0) ) {
|
// (progress._currentItems.size() == 0
|
||||||
|
// && progress._totalFileCount == 0) )
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
emit progressInfo( folder, progress );
|
emit progressInfo( folder, progress );
|
||||||
|
|
|
@ -39,6 +39,9 @@ namespace Progress
|
||||||
struct Info {
|
struct Info {
|
||||||
Info() : _totalFileCount(0), _totalSize(0), _completedFileCount(0), _completedSize(0) {}
|
Info() : _totalFileCount(0), _totalSize(0), _completedFileCount(0), _completedSize(0) {}
|
||||||
|
|
||||||
|
// Used during local and remote update phase
|
||||||
|
QString _currentDiscoveredFolder;
|
||||||
|
|
||||||
quint64 _totalFileCount;
|
quint64 _totalFileCount;
|
||||||
quint64 _totalSize;
|
quint64 _totalSize;
|
||||||
quint64 _completedFileCount;
|
quint64 _completedFileCount;
|
||||||
|
|
|
@ -41,6 +41,7 @@
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
#include <QSslCertificate>
|
#include <QSslCertificate>
|
||||||
#include <QProcess>
|
#include <QProcess>
|
||||||
|
#include <QElapsedTimer>
|
||||||
|
|
||||||
namespace Mirall {
|
namespace Mirall {
|
||||||
|
|
||||||
|
@ -452,6 +453,24 @@ void SyncEngine::handleSyncError(CSYNC *ctx, const char *state) {
|
||||||
finalize();
|
finalize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void update_job_update_callback (bool local,
|
||||||
|
const char *dirUrl,
|
||||||
|
void *userdata)
|
||||||
|
{
|
||||||
|
// Don't wanna overload the UI
|
||||||
|
static QElapsedTimer throttleTimer;
|
||||||
|
if (throttleTimer.elapsed() < 200) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
throttleTimer.restart();
|
||||||
|
|
||||||
|
UpdateJob *updateJob = static_cast<Mirall::UpdateJob*>(userdata);
|
||||||
|
if (updateJob) {
|
||||||
|
QString path = QString::fromUtf8(dirUrl).section('/', -1);
|
||||||
|
emit updateJob->folderDiscovered(local, path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void SyncEngine::startSync()
|
void SyncEngine::startSync()
|
||||||
{
|
{
|
||||||
Q_ASSERT(!_syncRunning);
|
Q_ASSERT(!_syncRunning);
|
||||||
|
@ -534,11 +553,16 @@ void SyncEngine::startSync()
|
||||||
UpdateJob *job = new UpdateJob(_csync_ctx);
|
UpdateJob *job = new UpdateJob(_csync_ctx);
|
||||||
job->moveToThread(&_thread);
|
job->moveToThread(&_thread);
|
||||||
connect(job, SIGNAL(finished(int)), this, SLOT(slotUpdateFinished(int)));
|
connect(job, SIGNAL(finished(int)), this, SLOT(slotUpdateFinished(int)));
|
||||||
|
connect(job, SIGNAL(folderDiscovered(bool,QString)),
|
||||||
|
this, SIGNAL(folderDiscovered(bool,QString)));
|
||||||
QMetaObject::invokeMethod(job, "start", Qt::QueuedConnection);
|
QMetaObject::invokeMethod(job, "start", Qt::QueuedConnection);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SyncEngine::slotUpdateFinished(int updateResult)
|
void SyncEngine::slotUpdateFinished(int updateResult)
|
||||||
{
|
{
|
||||||
|
// To clean the progress info
|
||||||
|
emit folderDiscovered(false, QString());
|
||||||
|
|
||||||
if (updateResult < 0 ) {
|
if (updateResult < 0 ) {
|
||||||
handleSyncError(_csync_ctx, "csync_update");
|
handleSyncError(_csync_ctx, "csync_update");
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -27,6 +27,9 @@
|
||||||
|
|
||||||
#include <csync.h>
|
#include <csync.h>
|
||||||
|
|
||||||
|
// when do we go away with this private/public separation?
|
||||||
|
#include <csync_private.h>
|
||||||
|
|
||||||
#include "mirall/syncfileitem.h"
|
#include "mirall/syncfileitem.h"
|
||||||
#include "mirall/progressdispatcher.h"
|
#include "mirall/progressdispatcher.h"
|
||||||
#include "mirall/utility.h"
|
#include "mirall/utility.h"
|
||||||
|
@ -62,6 +65,9 @@ signals:
|
||||||
void csyncError( const QString& );
|
void csyncError( const QString& );
|
||||||
void csyncUnavailable();
|
void csyncUnavailable();
|
||||||
|
|
||||||
|
// During update, before reconcile
|
||||||
|
void folderDiscovered(bool local, QString folderUrl);
|
||||||
|
|
||||||
// before actual syncing (after update+reconcile) for each item
|
// before actual syncing (after update+reconcile) for each item
|
||||||
void syncItemDiscovered(const SyncFileItem&);
|
void syncItemDiscovered(const SyncFileItem&);
|
||||||
// after the above signals. with the items that actually need propagating
|
// after the above signals. with the items that actually need propagating
|
||||||
|
@ -141,6 +147,9 @@ private:
|
||||||
QHash<QString, QByteArray> _remotePerms;
|
QHash<QString, QByteArray> _remotePerms;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void update_job_update_callback (bool local,
|
||||||
|
const char *dirname,
|
||||||
|
void *userdata);
|
||||||
|
|
||||||
class UpdateJob : public QObject {
|
class UpdateJob : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
@ -152,6 +161,8 @@ class UpdateJob : public QObject {
|
||||||
csync_set_log_callback(_log_callback);
|
csync_set_log_callback(_log_callback);
|
||||||
csync_set_log_level(_log_level);
|
csync_set_log_level(_log_level);
|
||||||
csync_set_log_userdata(_log_userdata);
|
csync_set_log_userdata(_log_userdata);
|
||||||
|
_csync_ctx->callbacks.update_callback = update_job_update_callback;
|
||||||
|
_csync_ctx->callbacks.update_callback_userdata = this;
|
||||||
emit finished(csync_update(_csync_ctx));
|
emit finished(csync_update(_csync_ctx));
|
||||||
deleteLater();
|
deleteLater();
|
||||||
}
|
}
|
||||||
|
@ -165,6 +176,7 @@ public:
|
||||||
_log_userdata = csync_get_log_userdata();
|
_log_userdata = csync_get_log_userdata();
|
||||||
}
|
}
|
||||||
signals:
|
signals:
|
||||||
|
void folderDiscovered(bool local, QString folderUrl);
|
||||||
void finished(int result);
|
void finished(int result);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue