mirror of
https://github.com/nextcloud/desktop.git
synced 2024-11-29 20:28:57 +03:00
SelectiveSync: Show in-progress label #3524
We now show 'Fetching data...' after a second. This also increased the timeout to 60s, making the error condition much less likely.
This commit is contained in:
parent
a752eadd0f
commit
05eee16959
2 changed files with 124 additions and 44 deletions
|
@ -90,7 +90,7 @@ Qt::ItemFlags FolderStatusModel::flags ( const QModelIndex &index ) const
|
||||||
}
|
}
|
||||||
return Qt::ItemIsEnabled | ret;
|
return Qt::ItemIsEnabled | ret;
|
||||||
}
|
}
|
||||||
case ErrorLabel:
|
case FetchLabel:
|
||||||
return Qt::ItemIsEnabled
|
return Qt::ItemIsEnabled
|
||||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0)
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0)
|
||||||
| Qt::ItemNeverHasChildren
|
| Qt::ItemNeverHasChildren
|
||||||
|
@ -151,11 +151,20 @@ QVariant FolderStatusModel::data(const QModelIndex &index, int role) const
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return QVariant();
|
return QVariant();
|
||||||
case ErrorLabel:
|
case FetchLabel:
|
||||||
|
{
|
||||||
|
const auto x = static_cast<SubFolderInfo *>(index.internalPointer());
|
||||||
switch(role) {
|
switch(role) {
|
||||||
case Qt::DisplayRole: return tr("Error while loading the list of folders from the server.");
|
case Qt::DisplayRole:
|
||||||
|
if (x->_hasError) {
|
||||||
|
return tr("Error while loading the list of folders from the server.");
|
||||||
|
} else {
|
||||||
|
return tr("Fetching folder list from server...");
|
||||||
|
}
|
||||||
|
break;
|
||||||
default: return QVariant();
|
default: return QVariant();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
case RootFolder:
|
case RootFolder:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -302,7 +311,7 @@ int FolderStatusModel::rowCount(const QModelIndex& parent) const
|
||||||
auto info = infoForIndex(parent);
|
auto info = infoForIndex(parent);
|
||||||
if (!info)
|
if (!info)
|
||||||
return 0;
|
return 0;
|
||||||
if (info->_hasError)
|
if (info->hasLabel())
|
||||||
return 1;
|
return 1;
|
||||||
return info->_subs.count();
|
return info->_subs.count();
|
||||||
}
|
}
|
||||||
|
@ -310,7 +319,11 @@ int FolderStatusModel::rowCount(const QModelIndex& parent) const
|
||||||
FolderStatusModel::ItemType FolderStatusModel::classify(const QModelIndex& index) const
|
FolderStatusModel::ItemType FolderStatusModel::classify(const QModelIndex& index) const
|
||||||
{
|
{
|
||||||
if (auto sub = static_cast<SubFolderInfo*>(index.internalPointer())) {
|
if (auto sub = static_cast<SubFolderInfo*>(index.internalPointer())) {
|
||||||
return sub->_hasError ? ErrorLabel : SubFolder;
|
if (sub->hasLabel()) {
|
||||||
|
return FetchLabel;
|
||||||
|
} else {
|
||||||
|
return SubFolder;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (index.row() < _folders.count()) {
|
if (index.row() < _folders.count()) {
|
||||||
return RootFolder;
|
return RootFolder;
|
||||||
|
@ -323,8 +336,7 @@ FolderStatusModel::SubFolderInfo* FolderStatusModel::infoForIndex(const QModelIn
|
||||||
if (!index.isValid())
|
if (!index.isValid())
|
||||||
return 0;
|
return 0;
|
||||||
if (auto parentInfo = static_cast<SubFolderInfo*>(index.internalPointer())) {
|
if (auto parentInfo = static_cast<SubFolderInfo*>(index.internalPointer())) {
|
||||||
if (parentInfo->_hasError) {
|
if (parentInfo->hasLabel()) {
|
||||||
// Error label
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return &parentInfo->_subs[index.row()];
|
return &parentInfo->_subs[index.row()];
|
||||||
|
@ -384,20 +396,21 @@ QModelIndex FolderStatusModel::index(int row, int column, const QModelIndex& par
|
||||||
}
|
}
|
||||||
switch(classify(parent)) {
|
switch(classify(parent)) {
|
||||||
case AddButton:
|
case AddButton:
|
||||||
case ErrorLabel:
|
case FetchLabel:
|
||||||
return QModelIndex();
|
return QModelIndex();
|
||||||
case RootFolder:
|
case RootFolder:
|
||||||
if (_folders.count() <= parent.row())
|
if (_folders.count() <= parent.row())
|
||||||
return QModelIndex(); // should not happen
|
return QModelIndex(); // should not happen
|
||||||
return createIndex(row, column, const_cast<SubFolderInfo *>(&_folders[parent.row()]));
|
return createIndex(row, column, const_cast<SubFolderInfo *>(&_folders[parent.row()]));
|
||||||
case SubFolder: {
|
case SubFolder: {
|
||||||
auto info = static_cast<SubFolderInfo*>(parent.internalPointer());
|
auto pinfo = static_cast<SubFolderInfo*>(parent.internalPointer());
|
||||||
if (info->_subs.count() <= parent.row())
|
if (pinfo->_subs.count() <= parent.row())
|
||||||
return QModelIndex(); // should not happen
|
return QModelIndex(); // should not happen
|
||||||
if (!info->_subs.at(parent.row())._hasError
|
auto & info = pinfo->_subs[parent.row()];
|
||||||
&& info->_subs.at(parent.row())._subs.count() <= row)
|
if (!info.hasLabel()
|
||||||
|
&& info._subs.count() <= row)
|
||||||
return QModelIndex(); // should not happen
|
return QModelIndex(); // should not happen
|
||||||
return createIndex(row, column, &info->_subs[parent.row()]);
|
return createIndex(row, column, &info);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return QModelIndex();
|
return QModelIndex();
|
||||||
|
@ -413,7 +426,7 @@ QModelIndex FolderStatusModel::parent(const QModelIndex& child) const
|
||||||
case AddButton:
|
case AddButton:
|
||||||
return QModelIndex();
|
return QModelIndex();
|
||||||
case SubFolder:
|
case SubFolder:
|
||||||
case ErrorLabel:
|
case FetchLabel:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
auto pathIdx = static_cast<SubFolderInfo*>(child.internalPointer())->_pathIdx;
|
auto pathIdx = static_cast<SubFolderInfo*>(child.internalPointer())->_pathIdx;
|
||||||
|
@ -467,7 +480,9 @@ void FolderStatusModel::fetchMore(const QModelIndex& parent)
|
||||||
if (!info || info->_fetched || info->_fetching)
|
if (!info || info->_fetched || info->_fetching)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
info->_hasError = false;
|
||||||
info->_fetching = true;
|
info->_fetching = true;
|
||||||
|
info->_fetchingLabel = false;
|
||||||
QString path = info->_folder->remotePath();
|
QString path = info->_folder->remotePath();
|
||||||
if (info->_path != QLatin1String("/")) {
|
if (info->_path != QLatin1String("/")) {
|
||||||
if (!path.endsWith(QLatin1Char('/'))) {
|
if (!path.endsWith(QLatin1Char('/'))) {
|
||||||
|
@ -477,13 +492,18 @@ void FolderStatusModel::fetchMore(const QModelIndex& parent)
|
||||||
}
|
}
|
||||||
LsColJob *job = new LsColJob(_accountState->account(), path, this);
|
LsColJob *job = new LsColJob(_accountState->account(), path, this);
|
||||||
job->setProperties(QList<QByteArray>() << "resourcetype" << "quota-used-bytes");
|
job->setProperties(QList<QByteArray>() << "resourcetype" << "quota-used-bytes");
|
||||||
job->setTimeout(5 * 1000);
|
job->setTimeout(60 * 1000);
|
||||||
connect(job, SIGNAL(directoryListingSubfolders(QStringList)),
|
connect(job, SIGNAL(directoryListingSubfolders(QStringList)),
|
||||||
SLOT(slotUpdateDirectories(QStringList)));
|
SLOT(slotUpdateDirectories(QStringList)));
|
||||||
connect(job, SIGNAL(finishedWithError(QNetworkReply*)),
|
connect(job, SIGNAL(finishedWithError(QNetworkReply*)),
|
||||||
this, SLOT(slotLscolFinishedWithError(QNetworkReply*)));
|
this, SLOT(slotLscolFinishedWithError(QNetworkReply*)));
|
||||||
job->start();
|
job->start();
|
||||||
job->setProperty(propertyParentIndexC , QVariant::fromValue<QPersistentModelIndex>(parent));
|
QPersistentModelIndex persistentIndex(parent);
|
||||||
|
job->setProperty(propertyParentIndexC , QVariant::fromValue(persistentIndex));
|
||||||
|
|
||||||
|
// Show 'fetching data...' hint after a while.
|
||||||
|
_fetchingItems[persistentIndex].start();
|
||||||
|
QTimer::singleShot(1000, this, SLOT(slotShowFetchProgress()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void FolderStatusModel::slotUpdateDirectories(const QStringList &list_)
|
void FolderStatusModel::slotUpdateDirectories(const QStringList &list_)
|
||||||
|
@ -496,12 +516,16 @@ void FolderStatusModel::slotUpdateDirectories(const QStringList &list_)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (parentInfo->_hasError) {
|
if (parentInfo->hasLabel()) {
|
||||||
beginRemoveRows(idx, 0 ,0);
|
beginRemoveRows(idx, 0 ,0);
|
||||||
parentInfo->_hasError = false;
|
parentInfo->_hasError = false;
|
||||||
|
parentInfo->_fetchingLabel = false;
|
||||||
endRemoveRows();
|
endRemoveRows();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
parentInfo->_fetching = false;
|
||||||
|
parentInfo->_fetched = true;
|
||||||
|
|
||||||
auto list = list_;
|
auto list = list_;
|
||||||
list.removeFirst(); // remove the parent item
|
list.removeFirst(); // remove the parent item
|
||||||
|
|
||||||
|
@ -522,9 +546,6 @@ void FolderStatusModel::slotUpdateDirectories(const QStringList &list_)
|
||||||
|
|
||||||
beginInsertRows(idx, 0, list.count() - 1);
|
beginInsertRows(idx, 0, list.count() - 1);
|
||||||
|
|
||||||
parentInfo->_fetched = true;
|
|
||||||
parentInfo->_fetching = false;
|
|
||||||
|
|
||||||
QStringList selectiveSyncBlackList;
|
QStringList selectiveSyncBlackList;
|
||||||
if (parentInfo->_checked == Qt::PartiallyChecked) {
|
if (parentInfo->_checked == Qt::PartiallyChecked) {
|
||||||
selectiveSyncBlackList = parentInfo->_folder->journalDb()->getSelectiveSyncList(SyncJournalDb::SelectiveSyncBlackList);
|
selectiveSyncBlackList = parentInfo->_folder->journalDb()->getSelectiveSyncList(SyncJournalDb::SelectiveSyncBlackList);
|
||||||
|
@ -589,14 +610,17 @@ void FolderStatusModel::slotLscolFinishedWithError(QNetworkReply* r)
|
||||||
}
|
}
|
||||||
auto parentInfo = infoForIndex(idx);
|
auto parentInfo = infoForIndex(idx);
|
||||||
if (parentInfo) {
|
if (parentInfo) {
|
||||||
parentInfo->_fetching = false;
|
|
||||||
if (r->error() == QNetworkReply::ContentNotFoundError) {
|
if (r->error() == QNetworkReply::ContentNotFoundError) {
|
||||||
parentInfo->_fetched = true;
|
parentInfo->_fetched = true;
|
||||||
} else if (!parentInfo->_hasError) {
|
} else {
|
||||||
|
if (!parentInfo->hasLabel()) {
|
||||||
beginInsertRows(idx, 0, 0);
|
beginInsertRows(idx, 0, 0);
|
||||||
parentInfo->_hasError = true;
|
|
||||||
endInsertRows();
|
endInsertRows();
|
||||||
}
|
}
|
||||||
|
parentInfo->_hasError = true;
|
||||||
|
}
|
||||||
|
parentInfo->_fetching = false;
|
||||||
|
parentInfo->_fetchingLabel = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -900,13 +924,8 @@ void FolderStatusModel::slotFolderSyncStateChange(Folder *f)
|
||||||
if (i->_isDirectory && (i->_instruction == CSYNC_INSTRUCTION_NEW
|
if (i->_isDirectory && (i->_instruction == CSYNC_INSTRUCTION_NEW
|
||||||
|| i->_instruction == CSYNC_INSTRUCTION_REMOVE)) {
|
|| i->_instruction == CSYNC_INSTRUCTION_REMOVE)) {
|
||||||
// There is a new or a removed folder. reset all data
|
// There is a new or a removed folder. reset all data
|
||||||
_folders[folderIndex]._fetched = false;
|
auto & info = _folders[folderIndex];
|
||||||
_folders[folderIndex]._fetching = false;
|
info.resetSubs(this, index(folderIndex));
|
||||||
if (!_folders.at(folderIndex)._subs.isEmpty()) {
|
|
||||||
beginRemoveRows(index(folderIndex), 0, _folders.at(folderIndex)._subs.count() - 1);
|
|
||||||
_folders[folderIndex]._subs.clear();
|
|
||||||
endRemoveRows();
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -942,17 +961,53 @@ void FolderStatusModel::slotNewBigFolder()
|
||||||
}
|
}
|
||||||
if (folderIndex < 0) { return; }
|
if (folderIndex < 0) { return; }
|
||||||
|
|
||||||
_folders[folderIndex]._fetched = false;
|
_folders[folderIndex].resetSubs(this, index(folderIndex));
|
||||||
_folders[folderIndex]._fetching = false;
|
|
||||||
if (!_folders.at(folderIndex)._subs.isEmpty()) {
|
|
||||||
beginRemoveRows(index(folderIndex), 0, _folders.at(folderIndex)._subs.count() - 1);
|
|
||||||
_folders[folderIndex]._subs.clear();
|
|
||||||
endRemoveRows();
|
|
||||||
}
|
|
||||||
|
|
||||||
emit suggestExpand(index(folderIndex));
|
emit suggestExpand(index(folderIndex));
|
||||||
emit dirtyChanged();
|
emit dirtyChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FolderStatusModel::slotShowFetchProgress()
|
||||||
|
{
|
||||||
|
QMutableMapIterator<QPersistentModelIndex, QElapsedTimer> it(_fetchingItems);
|
||||||
|
while (it.hasNext()) {
|
||||||
|
it.next();
|
||||||
|
if (it.value().elapsed() > 800)
|
||||||
|
{
|
||||||
|
auto idx = it.key();
|
||||||
|
auto* info = infoForIndex(idx);
|
||||||
|
if (info && info->_fetching) {
|
||||||
|
if (!info->hasLabel()) {
|
||||||
|
beginInsertRows(idx, 0, 0);
|
||||||
|
endInsertRows();
|
||||||
|
}
|
||||||
|
info->_fetchingLabel = true;
|
||||||
|
}
|
||||||
|
it.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FolderStatusModel::SubFolderInfo::hasLabel() const
|
||||||
|
{
|
||||||
|
return _hasError || _fetchingLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FolderStatusModel::SubFolderInfo::resetSubs(FolderStatusModel* model, QModelIndex index)
|
||||||
|
{
|
||||||
|
_fetched = false;
|
||||||
|
_fetching = false;
|
||||||
|
if (hasLabel()) {
|
||||||
|
model->beginRemoveRows(index, 0 ,0);
|
||||||
|
_fetchingLabel = false;
|
||||||
|
_hasError = false;
|
||||||
|
model->endRemoveRows();
|
||||||
|
} else if (!_subs.isEmpty()) {
|
||||||
|
model->beginRemoveRows(index, 0, _subs.count() - 1);
|
||||||
|
_subs.clear();
|
||||||
|
model->endRemoveRows();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
} // namespace OCC
|
} // namespace OCC
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
#include <accountfwd.h>
|
#include <accountfwd.h>
|
||||||
#include <QAbstractItemModel>
|
#include <QAbstractItemModel>
|
||||||
#include <QVector>
|
#include <QVector>
|
||||||
|
#include <QElapsedTimer>
|
||||||
|
|
||||||
class QNetworkReply;
|
class QNetworkReply;
|
||||||
namespace OCC {
|
namespace OCC {
|
||||||
|
@ -51,20 +51,30 @@ public:
|
||||||
|
|
||||||
struct SubFolderInfo {
|
struct SubFolderInfo {
|
||||||
SubFolderInfo()
|
SubFolderInfo()
|
||||||
: _folder(0), _size(0), _fetched(false), _fetching(false), _isUndecided(false),
|
: _folder(0), _size(0), _fetched(false), _fetching(false),
|
||||||
_hasError(false), _checked(Qt::Checked) {}
|
_hasError(false), _fetchingLabel(false), _isUndecided(false), _checked(Qt::Checked) {}
|
||||||
Folder *_folder;
|
Folder *_folder;
|
||||||
QString _name;
|
QString _name;
|
||||||
QString _path;
|
QString _path;
|
||||||
QVector<int> _pathIdx;
|
QVector<int> _pathIdx;
|
||||||
QVector<SubFolderInfo> _subs;
|
QVector<SubFolderInfo> _subs;
|
||||||
qint64 _size;
|
qint64 _size;
|
||||||
|
|
||||||
bool _fetched; // If we did the LSCOL for this folder already
|
bool _fetched; // If we did the LSCOL for this folder already
|
||||||
bool _fetching;
|
bool _fetching; // Whether a LSCOL job is currently running
|
||||||
bool _isUndecided; // undecided folders are the big folders that the user has not accepted yet
|
|
||||||
bool _hasError; // If the last fetching job ended in an error
|
bool _hasError; // If the last fetching job ended in an error
|
||||||
|
bool _fetchingLabel; // Whether a 'fetching in progress' label is shown.
|
||||||
|
|
||||||
|
bool _isUndecided; // undecided folders are the big folders that the user has not accepted yet
|
||||||
|
|
||||||
Qt::CheckState _checked;
|
Qt::CheckState _checked;
|
||||||
|
|
||||||
|
// Whether this has a FetchLabel subrow
|
||||||
|
bool hasLabel() const;
|
||||||
|
|
||||||
|
// Reset all subfolders and fetch status
|
||||||
|
void resetSubs(FolderStatusModel* model, QModelIndex index);
|
||||||
|
|
||||||
struct Progress {
|
struct Progress {
|
||||||
Progress() : _warningCount(0), _overallPercent(0) {}
|
Progress() : _warningCount(0), _overallPercent(0) {}
|
||||||
bool isNull() const
|
bool isNull() const
|
||||||
|
@ -79,7 +89,7 @@ public:
|
||||||
|
|
||||||
QVector<SubFolderInfo> _folders;
|
QVector<SubFolderInfo> _folders;
|
||||||
|
|
||||||
enum ItemType { RootFolder, SubFolder, AddButton, ErrorLabel };
|
enum ItemType { RootFolder, SubFolder, AddButton, FetchLabel };
|
||||||
ItemType classify(const QModelIndex &index) const;
|
ItemType classify(const QModelIndex &index) const;
|
||||||
SubFolderInfo *infoForIndex(const QModelIndex &index) const;
|
SubFolderInfo *infoForIndex(const QModelIndex &index) const;
|
||||||
|
|
||||||
|
@ -105,12 +115,25 @@ private slots:
|
||||||
void slotFolderScheduleQueueChanged();
|
void slotFolderScheduleQueueChanged();
|
||||||
void slotNewBigFolder();
|
void slotNewBigFolder();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* "In progress" labels for fetching data from the server are only
|
||||||
|
* added after some time to avoid popping.
|
||||||
|
*/
|
||||||
|
void slotShowFetchProgress();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QStringList createBlackList(OCC::FolderStatusModel::SubFolderInfo* root,
|
QStringList createBlackList(OCC::FolderStatusModel::SubFolderInfo* root,
|
||||||
const QStringList& oldBlackList) const;
|
const QStringList& oldBlackList) const;
|
||||||
const AccountState* _accountState;
|
const AccountState* _accountState;
|
||||||
bool _dirty; // If the selective sync checkboxes were changed
|
bool _dirty; // If the selective sync checkboxes were changed
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Keeps track of items that are fetching data from the server.
|
||||||
|
*
|
||||||
|
* See slotShowPendingFetchProgress()
|
||||||
|
*/
|
||||||
|
QMap<QPersistentModelIndex, QElapsedTimer> _fetchingItems;
|
||||||
|
|
||||||
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
|
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
|
||||||
//the roles argument was added in Qt5
|
//the roles argument was added in Qt5
|
||||||
void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles = QVector<int>())
|
void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles = QVector<int>())
|
||||||
|
@ -120,6 +143,8 @@ private:
|
||||||
signals:
|
signals:
|
||||||
void dirtyChanged();
|
void dirtyChanged();
|
||||||
void suggestExpand(const QModelIndex &); // Tell the view that this item should be expanded because it has an undecided item
|
void suggestExpand(const QModelIndex &); // Tell the view that this item should be expanded because it has an undecided item
|
||||||
|
|
||||||
|
friend class SubFolderInfo;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace OCC
|
} // namespace OCC
|
||||||
|
|
Loading…
Reference in a new issue