mirror of
https://github.com/nextcloud/desktop.git
synced 2024-11-25 06:25:56 +03:00
Merges errors and protocols into notifications/activities.
- Errors will always be on top, then Notifications comes next and then Activities for last. - Adds 2 new types of Activity: NotificationType and ErrorType. - ActivityListModel and ActivityWidget stores AccountState to connect user and widget. Signed-off-by: Camila San <hello@camila.codes>
This commit is contained in:
parent
d0c72dd642
commit
a9cd3b3a6d
9 changed files with 188 additions and 26 deletions
|
@ -24,6 +24,8 @@
|
|||
<file>resources/delete.png</file>
|
||||
<file>resources/close.svg</file>
|
||||
<file>resources/bell.svg</file>
|
||||
<file>resources/link.svg</file>
|
||||
<file>resources/files.svg</file>
|
||||
</qresource>
|
||||
<qresource prefix="/"/>
|
||||
</RCC>
|
||||
|
|
1
resources/files.svg
Normal file
1
resources/files.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16" version="1"><path d="M1.457 1.997c-.25 0-.46.21-.46.46v11.08c0 .26.202.46.46.46h13.08c.258 0 .46-.2.46-.46V4.46c0-.25-.21-.463-.46-.463h-6.54l-2-2z" /></svg>
|
After Width: | Height: | Size: 220 B |
1
resources/link.svg
Normal file
1
resources/link.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewbox="0 0 16 16"><path d="M7.4 0C3.3 0 0 3.3 0 7.4s3.3 7.4 7.4 7.4 7.4-3.3 7.4-7.4S11.5 0 7.4 0zm.8.9c1.3 0 2.4.8 3.5 1.3l1.8 2.5-.3 1.1.6.3V8.5c-.2.7-.6 1.3-.9 2-.2.1 0-.8-.1-1 0-.6-.5-.6-.9-.2-.4.3-1.4.3-1.5-.4-.3-.8 0-1.7.3-2.5l-.6-.7.2-1.8-.8-.9.2-1-1-.6c-.2-.2-.6-.2-.7-.4.1 0 .2-.1.2-.1zM5.6 1s.1 0 .1.1c.4.2-.1.4-.2.6-.5.3.3.7.5 1 .4-.1.8-.7 1.4-.5.7-.2.6.6 1.1 1 .1.2.9.8.4.6-.5-.4-1-.4-1.3.1-.8.5-.3-.9-.7-1.2-.6-.7-.4.5-.4.9-.4 0-1.1-.3-1.5.2l.4.6.5-.7c0-.3.1.2.3.3.1.2.8.7.3.9-.8.4-1.4 1.1-2.1 1.7-.2.5-.7.4-1 0-.7-.4-.7.7-.6 1.1l.6-.4v1.1c-.4.4-.9-.7-1.3-.9V5.9c0-.4-.1-.9 0-1.3.8-.9 1.7-1.9 2.2-3h.8c.6.2.3-.7.5-.6zM4.4 9.2c.1 0 .2 0 .3.1.8.1 1.4.7 2 1.1.5.5 1.6.3 1.7 1.2-.2.9-1.1 1.4-1.8 1.7-.2.1-.4.2-.6.2-.7.2-1-.6-1.2-1.1-.3-.7-1.1-1.2-1-2.1 0-.4.2-1 .6-1.1z"/></svg>
|
After Width: | Height: | Size: 851 B |
|
@ -49,7 +49,8 @@ public:
|
|||
|
||||
enum Type {
|
||||
ActivityType,
|
||||
NotificationType
|
||||
NotificationType,
|
||||
ErrorType
|
||||
};
|
||||
|
||||
Type _type;
|
||||
|
@ -60,6 +61,7 @@ public:
|
|||
QUrl _link;
|
||||
QDateTime _dateTime;
|
||||
QString _accName;
|
||||
int _status;
|
||||
|
||||
QVector<ActivityLink> _links;
|
||||
/**
|
||||
|
|
|
@ -159,6 +159,43 @@ void ActivityItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
|
|||
primaryButton.features |= QStyleOptionButton::DefaultButton;
|
||||
primaryButton.state |= QStyle::State_Raised;
|
||||
|
||||
// save info to be able to filter mouse clicks
|
||||
_buttonHeight = buttonSize;
|
||||
_spaceBetweenButtons = leftMargin;
|
||||
_primaryButtonWidth = primaryButton.rect.size().width();
|
||||
_secondaryButtonWidth = secondaryButton.rect.size().width();
|
||||
} else if(activityType == Activity::Type::ErrorType){
|
||||
int rightMargin = margin;
|
||||
int leftMargin = margin * offset;
|
||||
int top = option.rect.top() + margin - offset;
|
||||
int buttonSize = option.rect.height()/2.5;
|
||||
|
||||
// Secondary will be 'Dismiss' or '...'
|
||||
secondaryButton.rect = option.rect;
|
||||
secondaryButton.icon = QIcon(QLatin1String(":/client/resources/files.svg"));
|
||||
|
||||
int right = option.rect.right() - rightMargin;
|
||||
int left = right - buttonSize;
|
||||
secondaryButton.iconSize = QSize(buttonSize, buttonSize);
|
||||
secondaryButton.rect.setLeft(left);
|
||||
secondaryButton.rect.setRight(right);
|
||||
secondaryButton.rect.setTop(top);
|
||||
secondaryButton.rect.setHeight(_buttonHeight);
|
||||
secondaryButton.features |= QStyleOptionButton::DefaultButton;
|
||||
secondaryButton.state |= QStyle::State_Raised;
|
||||
|
||||
// Primary button will be 'More Information'
|
||||
primaryButton.rect = option.rect;
|
||||
primaryButton.text = tr("Open Browser");
|
||||
right = secondaryButton.rect.left() - rightMargin;
|
||||
left = secondaryButton.rect.left() - leftMargin;
|
||||
primaryButton.rect.setLeft(left - fm.width(primaryButton.text));
|
||||
primaryButton.rect.setRight(right);
|
||||
primaryButton.rect.setTop(top);
|
||||
primaryButton.rect.setHeight(_buttonHeight);
|
||||
primaryButton.features |= QStyleOptionButton::DefaultButton;
|
||||
primaryButton.state |= QStyle::State_Raised;
|
||||
|
||||
// save info to be able to filter mouse clicks
|
||||
_buttonHeight = buttonSize;
|
||||
_spaceBetweenButtons = leftMargin;
|
||||
|
@ -200,7 +237,7 @@ void ActivityItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
|
|||
painter->drawText(actionTextBox, elidedAction);
|
||||
|
||||
// draw the buttons
|
||||
if(activityType == Activity::Type::NotificationType){
|
||||
if(activityType == Activity::Type::NotificationType || activityType == Activity::Type::ErrorType){
|
||||
QApplication::style()->drawControl(QStyle::CE_PushButton, &primaryButton, painter);
|
||||
QApplication::style()->drawControl(QStyle::CE_PushButton, &secondaryButton, painter);
|
||||
}
|
||||
|
@ -235,7 +272,8 @@ void ActivityItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
|
|||
bool ActivityItemDelegate::editorEvent(QEvent *event, QAbstractItemModel *model,
|
||||
const QStyleOptionViewItem &option, const QModelIndex &index)
|
||||
{
|
||||
if(qvariant_cast<Activity::Type>(index.data(ActionRole)) == Activity::Type::NotificationType){
|
||||
if(qvariant_cast<Activity::Type>(index.data(ActionRole)) == Activity::Type::NotificationType
|
||||
|| qvariant_cast<Activity::Type>(index.data(ActionRole)) == Activity::Type::ErrorType){
|
||||
if (event->type() == QEvent::MouseButtonRelease){
|
||||
QMouseEvent *mouseEvent = (QMouseEvent*)event;
|
||||
if(mouseEvent){
|
||||
|
|
|
@ -29,6 +29,8 @@
|
|||
#include "activitydata.h"
|
||||
#include "activitylistmodel.h"
|
||||
|
||||
#include "theme.h"
|
||||
|
||||
#include "servernotificationhandler.h"
|
||||
|
||||
namespace OCC {
|
||||
|
@ -83,15 +85,24 @@ QVariant ActivityListModel::data(const QModelIndex &index, int role) const
|
|||
return customList;
|
||||
break;
|
||||
}
|
||||
// case ActivityItemDelegate::UserIconRole:
|
||||
// return QIcon(QLatin1String(":/client/resources/account.png"));
|
||||
// break;
|
||||
case ActivityItemDelegate::ActionIconRole:
|
||||
if(a._type == Activity::NotificationType){
|
||||
QIcon cachedIcon = ServerNotificationHandler::iconCache.value(a._id);
|
||||
if(!cachedIcon.isNull())
|
||||
return cachedIcon;
|
||||
else return QIcon(QLatin1String(":/client/resources/bell.svg"));
|
||||
} else if(a._type == Activity::ErrorType){
|
||||
if(a._status == SyncFileItem::NormalError
|
||||
|| a._status == SyncFileItem::FatalError
|
||||
|| a._status == SyncFileItem::DetailError
|
||||
|| a._status == SyncFileItem::BlacklistedError) {
|
||||
return Theme::instance()->syncStateIcon(SyncResult::Error);
|
||||
} else if(a._status == SyncFileItem::SoftError
|
||||
|| a._status == SyncFileItem::FileIgnored
|
||||
|| a._status == SyncFileItem::Conflict
|
||||
|| a._status == SyncFileItem::Restoration){
|
||||
return Theme::instance()->syncStateIcon(SyncResult::Problem);
|
||||
}
|
||||
} else return QIcon(QLatin1String(":/client/resources/activity.png"));
|
||||
return QVariant();
|
||||
break;
|
||||
|
@ -204,7 +215,12 @@ void ActivityListModel::slotActivitiesReceived(const QJsonDocument &json, int st
|
|||
combineActivityLists();
|
||||
}
|
||||
|
||||
void ActivityListModel::addToActivityList(Activity activity) {
|
||||
void ActivityListModel::addErrorToActivityList(Activity activity) {
|
||||
_notificationErrorsLists.prepend(activity);
|
||||
combineActivityLists();
|
||||
}
|
||||
|
||||
void ActivityListModel::addNotificationToActivityList(Activity activity) {
|
||||
_notificationLists.prepend(activity);
|
||||
combineActivityLists();
|
||||
}
|
||||
|
@ -219,6 +235,9 @@ void ActivityListModel::combineActivityLists()
|
|||
{
|
||||
ActivityList resultList;
|
||||
|
||||
std::sort(_notificationErrorsLists.begin(), _notificationErrorsLists.end());
|
||||
resultList.append(_notificationErrorsLists);
|
||||
|
||||
std::sort(_notificationLists.begin(), _notificationLists.end());
|
||||
resultList.append(_notificationLists);
|
||||
|
||||
|
|
|
@ -47,7 +47,8 @@ public:
|
|||
void fetchMore(const QModelIndex &) Q_DECL_OVERRIDE;
|
||||
|
||||
ActivityList activityList() { return _finalList; }
|
||||
void addToActivityList(Activity activity);
|
||||
void addNotificationToActivityList(Activity activity);
|
||||
void addErrorToActivityList(Activity activity);
|
||||
void removeFromActivityList(int row);
|
||||
|
||||
public slots:
|
||||
|
@ -66,6 +67,7 @@ private:
|
|||
|
||||
ActivityList _activityLists;
|
||||
ActivityList _notificationLists;
|
||||
ActivityList _notificationErrorsLists;
|
||||
ActivityList _finalList;
|
||||
AccountState *_accountState;
|
||||
bool _currentlyFetching = true;
|
||||
|
|
|
@ -95,11 +95,17 @@ ActivityWidget::ActivityWidget(AccountState *accountState, QWidget *parent)
|
|||
|
||||
connect(delegate, &ActivityItemDelegate::primaryButtonClickedOnItemView, this, &ActivityWidget::slotPrimaryButtonClickedOnListView);
|
||||
connect(delegate, &ActivityItemDelegate::secondaryButtonClickedOnItemView, this, &ActivityWidget::slotSecondaryButtonClickedOnListView);
|
||||
//connect(this, &ActivityWidget::sendNotificationRequest, this, &ActivityWidget::slotSendNotificationRequest);
|
||||
|
||||
connect(_ui->_activityList, &QListView::activated, this, &ActivityWidget::slotOpenFile);
|
||||
|
||||
connect(&_removeTimer, &QTimer::timeout, this, &ActivityWidget::slotCheckToCleanWidgets);
|
||||
|
||||
connect(ProgressDispatcher::instance(), &ProgressDispatcher::progressInfo,
|
||||
this, &ActivityWidget::slotProgressInfo);
|
||||
connect(ProgressDispatcher::instance(), &ProgressDispatcher::itemCompleted,
|
||||
this, &ActivityWidget::slotItemCompleted);
|
||||
connect(ProgressDispatcher::instance(), &ProgressDispatcher::syncError,
|
||||
this, &ActivityWidget::addError);
|
||||
|
||||
_removeTimer.setInterval(1000);
|
||||
}
|
||||
|
||||
|
@ -108,6 +114,75 @@ ActivityWidget::~ActivityWidget()
|
|||
delete _ui;
|
||||
}
|
||||
|
||||
void ActivityWidget::slotProgressInfo(const QString &folder, const ProgressInfo &progress)
|
||||
{
|
||||
if (progress.status() == ProgressInfo::Starting) {
|
||||
// The sync is restarting, clean the old items
|
||||
//cleanItems(folder);
|
||||
}
|
||||
}
|
||||
|
||||
void ActivityWidget::slotItemCompleted(const QString &folder, const SyncFileItemPtr &item){
|
||||
auto folderInstance = FolderMan::instance()->folder(folder);
|
||||
if (!folderInstance)
|
||||
return;
|
||||
|
||||
// check if we are adding it to the right account and if it is useful information (error)
|
||||
if(folderInstance->accountState() == _accountState){
|
||||
Activity activity;
|
||||
activity._type = Activity::ErrorType;
|
||||
activity._dateTime = QDateTime::fromString(QDateTime::currentDateTime().toString(), Qt::ISODate);
|
||||
activity._subject = item->_errorString;
|
||||
qDebug() << "TOTAL " << folder;
|
||||
activity._message = item->_originalFile;
|
||||
activity._link = folderInstance->remotePath();
|
||||
activity._status = item->_status;
|
||||
activity._accName = folderInstance->accountState()->account()->displayName();
|
||||
activity._file = item->_file;
|
||||
|
||||
ActivityLink al;
|
||||
al._label = tr("Open Folder");
|
||||
al._link = folderInstance->path();
|
||||
al._verb = "";
|
||||
al._isPrimary = true;
|
||||
activity._links.append(al);
|
||||
|
||||
_model->addErrorToActivityList(activity);
|
||||
// add error widget
|
||||
}
|
||||
}
|
||||
|
||||
void ActivityWidget::addError(const QString &folderAlias, const QString &message,
|
||||
ErrorCategory category)
|
||||
{
|
||||
auto folderInstance = FolderMan::instance()->folder(folderAlias);
|
||||
if (!folderInstance)
|
||||
return;
|
||||
|
||||
if(folderInstance->accountState() == _accountState){
|
||||
Activity activity;
|
||||
activity._type = Activity::ErrorType;
|
||||
activity._dateTime = QDateTime::fromString(QDateTime::currentDateTime().toString(), Qt::ISODate);
|
||||
activity._subject = message;
|
||||
activity._message = folderInstance->shortGuiLocalPath();
|
||||
activity._link = folderInstance->shortGuiLocalPath();
|
||||
activity._status = SyncResult::Error;
|
||||
activity._accName = folderInstance->accountState()->account()->displayName();
|
||||
|
||||
if (category == ErrorCategory::InsufficientRemoteStorage) {
|
||||
ActivityLink link;
|
||||
link._label = tr("Retry all uploads");
|
||||
link._link = folderInstance->path();
|
||||
link._verb = "";
|
||||
link._isPrimary = true;
|
||||
activity._links.append(link);
|
||||
}
|
||||
|
||||
_model->addErrorToActivityList(activity);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ActivityWidget::slotPrimaryButtonClickedOnListView(const QModelIndex &index){
|
||||
QUrl link = qvariant_cast<QString>(index.data(ActivityItemDelegate::LinkRole));
|
||||
if(!link.isEmpty())
|
||||
|
@ -121,20 +196,32 @@ void ActivityWidget::slotSecondaryButtonClickedOnListView(const QModelIndex &ind
|
|||
actionLinks << qvariant_cast<ActivityLink>(customItem);
|
||||
}
|
||||
|
||||
const QString accountName = index.data(ActivityItemDelegate::AccountRole).toString();
|
||||
if(actionLinks.size() == 1){
|
||||
if(actionLinks.at(0)._verb == "DELETE")
|
||||
slotSendNotificationRequest(index.data(ActivityItemDelegate::AccountRole).toString(), actionLinks.at(0)._link, actionLinks.at(0)._verb, index.row());
|
||||
} else if(actionLinks.size() > 1){
|
||||
QMenu menu;
|
||||
foreach (ActivityLink actionLink, actionLinks) {
|
||||
QAction *menuAction = new QAction(actionLink._label, &menu);
|
||||
connect(menuAction, &QAction::triggered, this, [this, index, accountName, actionLink] {
|
||||
this->slotSendNotificationRequest(accountName, actionLink._link, actionLink._verb, index.row());
|
||||
});
|
||||
menu.addAction(menuAction);
|
||||
if(qvariant_cast<Activity::Type>(index.data(ActivityItemDelegate::ActionRole)) == Activity::Type::NotificationType){
|
||||
const QString accountName = index.data(ActivityItemDelegate::AccountRole).toString();
|
||||
if(actionLinks.size() == 1){
|
||||
if(actionLinks.at(0)._verb == "DELETE")
|
||||
slotSendNotificationRequest(index.data(ActivityItemDelegate::AccountRole).toString(), actionLinks.at(0)._link, actionLinks.at(0)._verb, index.row());
|
||||
} else if(actionLinks.size() > 1){
|
||||
QMenu menu;
|
||||
foreach (ActivityLink actionLink, actionLinks) {
|
||||
QAction *menuAction = new QAction(actionLink._label, &menu);
|
||||
connect(menuAction, &QAction::triggered, this, [this, index, accountName, actionLink] {
|
||||
this->slotSendNotificationRequest(accountName, actionLink._link, actionLink._verb, index.row());
|
||||
});
|
||||
menu.addAction(menuAction);
|
||||
}
|
||||
menu.exec(QCursor::pos());
|
||||
}
|
||||
}
|
||||
|
||||
if(qvariant_cast<Activity::Type>(index.data(ActivityItemDelegate::ActionRole)) == Activity::Type::ErrorType){
|
||||
QString fileName = index.data(ActivityItemDelegate::PathRole).toString();
|
||||
if (Folder *folder = FolderMan::instance()->folderForPath(actionLinks.first()._link)) {
|
||||
QString fullPath = folder->path() + fileName;
|
||||
if (QFile(fullPath).exists()) {
|
||||
showInFileManager(fullPath);
|
||||
}
|
||||
}
|
||||
menu.exec(QCursor::pos());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -369,7 +456,7 @@ void ActivityWidget::slotBuildNotificationDisplay(const ActivityList &list)
|
|||
}
|
||||
}
|
||||
|
||||
_model->addToActivityList(activity);
|
||||
_model->addNotificationToActivityList(activity);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -566,11 +653,13 @@ ActivitySettings::ActivitySettings(AccountState *accountState, QWidget *parent)
|
|||
connect(_activityWidget, &ActivityWidget::guiLog, this, &ActivitySettings::guiLog);
|
||||
connect(_activityWidget, &ActivityWidget::newNotification, this, &ActivitySettings::slotShowActivityTab);
|
||||
|
||||
// _protocolWidget = new ProtocolWidget(this);
|
||||
_protocolWidget = new ProtocolWidget(this);
|
||||
_vbox->addWidget(_protocolWidget);
|
||||
// _protocolTabId = _tab->addTab(_protocolWidget, Theme::instance()->syncStateIcon(SyncResult::Success), tr("Sync Protocol"));
|
||||
// connect(_protocolWidget, &ProtocolWidget::copyToClipboard, this, &ActivitySettings::slotCopyToClipboard);
|
||||
|
||||
// _issuesWidget = new IssuesWidget(this);
|
||||
_issuesWidget = new IssuesWidget(this);
|
||||
_vbox->addWidget(_issuesWidget);
|
||||
// _syncIssueTabId = _tab->addTab(_issuesWidget, Theme::instance()->syncStateIcon(SyncResult::Problem), QString());
|
||||
// slotShowIssueItemCount(0); // to display the label.
|
||||
// connect(_issuesWidget, &IssuesWidget::issueCountUpdated,
|
||||
|
|
|
@ -79,6 +79,12 @@ public slots:
|
|||
void slotRemoveAccount();
|
||||
void slotAccountActivityStatus(int statusCode);
|
||||
void slotRequestCleanupAndBlacklist(const Activity &blacklistActivity);
|
||||
//
|
||||
void addError(const QString &folderAlias, const QString &message, ErrorCategory category);
|
||||
void slotProgressInfo(const QString &folder, const ProgressInfo &progress);
|
||||
void slotItemCompleted(const QString &folder, const SyncFileItemPtr &item);
|
||||
//void slotOpenFile(QTreeWidgetItem *item, int);
|
||||
|
||||
|
||||
signals:
|
||||
void guiLog(const QString &, const QString &);
|
||||
|
@ -164,6 +170,8 @@ private:
|
|||
bool event(QEvent *e) Q_DECL_OVERRIDE;
|
||||
|
||||
ActivityWidget *_activityWidget;
|
||||
ProtocolWidget *_protocolWidget;
|
||||
IssuesWidget *_issuesWidget;
|
||||
QProgressIndicator *_progressIndicator;
|
||||
QTimer _notificationCheckTimer;
|
||||
QHash<AccountState *, QElapsedTimer> _timeSinceLastCheck;
|
||||
|
|
Loading…
Reference in a new issue