From 1fe5d6bb0cfb6d556b8eb6bf7b35a668cdcff243 Mon Sep 17 00:00:00 2001 From: Klaas Freitag Date: Wed, 23 Mar 2016 16:59:03 +0100 Subject: [PATCH] Notifications: Handle Notifications without an action. The policy that was said is that if a notification has no action, the client can and should display a close-button. This patch does it. In additon to that, the client needs a blacklist of closed notifcations otherwise they would re-appear next time the server notifications are fetched again. Also, changed the cleanup of not-longer-used widgets to be more robust. --- src/gui/activitywidget.cpp | 84 +++++++++++++++++++++++++++------- src/gui/activitywidget.h | 10 +++- src/gui/notificationwidget.cpp | 44 +++++++++++------- src/gui/notificationwidget.h | 2 +- 4 files changed, 103 insertions(+), 37 deletions(-) diff --git a/src/gui/activitywidget.cpp b/src/gui/activitywidget.cpp index 08b91955de..9b7c7cd0d4 100644 --- a/src/gui/activitywidget.cpp +++ b/src/gui/activitywidget.cpp @@ -94,6 +94,9 @@ ActivityWidget::ActivityWidget(QWidget *parent) : connect( _ui->_activityList, SIGNAL(activated(QModelIndex)), this, SLOT(slotOpenFile(QModelIndex))); + + connect( &_removeTimer, SIGNAL(timeout()), this, SLOT(slotCheckToCleanWidgets()) ); + _removeTimer.setInterval(1000); } ActivityWidget::~ActivityWidget() @@ -231,18 +234,26 @@ void ActivityWidget::slotBuildNotificationDisplay(const ActivityList& list) QString listAccountName; foreach( auto activity, list ) { + if( _blacklistedActivities.contains(activity)) { + qDebug() << Q_FUNC_INFO << "Activity in blacklist, skip"; + continue; + } + NotificationWidget *widget = 0; - if( _widgetForNotifId.contains(activity._id) ) { - widget = _widgetForNotifId[activity._id]; + if( _widgetForNotifId.contains( activity.ident()) ) { + widget = _widgetForNotifId[activity.ident()]; } else { widget = new NotificationWidget(this); connect(widget, SIGNAL(sendNotificationRequest(QString, QString, QString)), this, SLOT(slotSendNotificationRequest(QString, QString, QString))); + connect(widget, SIGNAL(requestCleanupAndBlacklist(Activity)), + this, SLOT(slotRequestCleanupAndBlacklist(Activity))); + _notificationsLayout->addWidget(widget); // _ui->_notifyScroll->setMinimumHeight( widget->height()); _ui->_notifyScroll->setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContentsOnFirstShow); - _widgetForNotifId[activity._id] = widget; + _widgetForNotifId[activity.ident()] = widget; } widget->setActivity( activity ); @@ -280,19 +291,20 @@ void ActivityWidget::slotBuildNotificationDisplay(const ActivityList& list) } } - // check if we have widgets that have no corresponding activity from + // check if there are widgets that have no corresponding activity from // the server any more. Collect them in a list - QList strayCats; + QList< Activity::Identifier > strayCats; foreach( auto id, _widgetForNotifId.keys() ) { - bool found = false; NotificationWidget *widget = _widgetForNotifId[id]; + bool found = false; // do not mark widgets of other accounts to delete. - if( widget->accountName() != listAccountName ) { + if( widget->activity()._accName != listAccountName ) { continue; } + foreach( auto activity, list ) { - if( activity._id == id ) { + if( activity.ident() == id ) { // found an activity found = true; break; @@ -307,8 +319,7 @@ void ActivityWidget::slotBuildNotificationDisplay(const ActivityList& list) // .. and now delete all these stray cat widgets. foreach( auto strayCatId, strayCats ) { NotificationWidget *widgetToGo = _widgetForNotifId[strayCatId]; - widgetToGo->deleteLater(); - _widgetForNotifId.remove(strayCatId); + scheduleWidgetToRemove(widgetToGo, 0); } _ui->_notifyLabel->setHidden( _widgetForNotifId.isEmpty() ); @@ -410,21 +421,60 @@ void ActivityWidget::slotNotifyServerFinished( const QString& reply, int replyCo // Add 200 millisecs to the predefined value to make sure that the timer in // widget's method readyToClose() has elapsed. if( replyCode == OCS_SUCCESS_STATUS_CODE ) { - QTimer::singleShot(NOTIFICATION_WIDGET_CLOSE_AFTER_MILLISECS+200, this, SLOT(slotCleanWidgetList())); + scheduleWidgetToRemove( job->widget() ); } } -void ActivityWidget::slotCleanWidgetList() +// blacklist the activity coming in here. +void ActivityWidget::slotRequestCleanupAndBlacklist(const Activity& blacklistActivity) { - foreach( int id, _widgetForNotifId.keys() ) { - Q_ASSERT(_widgetForNotifId[id]); - if( _widgetForNotifId[id]->readyToClose() ) { - auto *widget = _widgetForNotifId[id]; + if ( ! _blacklistedActivities.contains(blacklistActivity) ) { + _blacklistedActivities.append(blacklistActivity); + } + + NotificationWidget *widget = _widgetForNotifId[ blacklistActivity.ident() ]; + scheduleWidgetToRemove(widget); +} + +void ActivityWidget::scheduleWidgetToRemove(NotificationWidget *widget, int milliseconds) +{ + if( !widget ) { + return; + } + // in fife seconds from now, remove the widget. + QDateTime removeTime = QDateTime::currentDateTime().addMSecs(milliseconds); + + QPair removeInfo = qMakePair(removeTime, widget); + if( !_widgetsToRemove.contains(removeInfo) ) { + _widgetsToRemove.insert( removeInfo ); + if( !_removeTimer.isActive() ) { + _removeTimer.start(); + } + } +} + +// Called every second to see if widgets need to be removed. +void ActivityWidget::slotCheckToCleanWidgets() +{ + // loop over all widgets in the to-remove queue + foreach( auto toRemove, _widgetsToRemove ) { + QDateTime t = toRemove.first; + NotificationWidget *widget = toRemove.second; + + if( QDateTime::currentDateTime() > t ) { + // found one to remove! + Activity::Identifier id = widget->activity().ident(); _widgetForNotifId.remove(id); - delete widget; + widget->deleteLater(); + _widgetsToRemove.remove(toRemove); } } + if( _widgetsToRemove.isEmpty() ) { + _removeTimer.stop(); + } + + // check to see if the whole notification pane should be hidden if( _widgetForNotifId.isEmpty() ) { _ui->_notifyLabel->setHidden(true); _ui->_notifyScroll->setHidden(true); diff --git a/src/gui/activitywidget.h b/src/gui/activitywidget.h index cf6d73202a..a070a6ff5a 100644 --- a/src/gui/activitywidget.h +++ b/src/gui/activitywidget.h @@ -66,6 +66,7 @@ public slots: void slotRefreshNotifications(AccountState *ptr); void slotRemoveAccount( AccountState *ptr ); void slotAccountActivityStatus(AccountState *ast, int statusCode); + void slotRequestCleanupAndBlacklist(const Activity& blacklistActivity); signals: void guiLog(const QString&, const QString&); @@ -80,7 +81,8 @@ private slots: void slotNotifyNetworkError( QNetworkReply* ); void slotNotifyServerFinished( const QString& reply, int replyCode ); void endNotificationRequest(NotificationWidget *widget , int replyCode); - void slotCleanWidgetList(); + void scheduleWidgetToRemove(NotificationWidget *widget, int milliseconds = 4500); + void slotCheckToCleanWidgets(); private: void showLabels(); @@ -89,9 +91,13 @@ private: QPushButton *_copyBtn; QSet _accountsWithoutActivities; - QMap _widgetForNotifId; + QMap _widgetForNotifId; QElapsedTimer _guiLogTimer; QSet _guiLoggedNotifications; + ActivityList _blacklistedActivities; + + QSet< QPair > _widgetsToRemove; + QTimer _removeTimer; // number of currently running notification requests. If non zero, // no query for notifications is started. diff --git a/src/gui/notificationwidget.cpp b/src/gui/notificationwidget.cpp index 88217f1369..15dd7fc90a 100644 --- a/src/gui/notificationwidget.cpp +++ b/src/gui/notificationwidget.cpp @@ -54,13 +54,22 @@ void NotificationWidget::setActivity(const Activity& activity) foreach( auto button, _ui._buttonBox->buttons() ) { _ui._buttonBox->removeButton(button); } + _buttons.clear(); // display buttons for the links - foreach( auto link, activity._links ) { - QPushButton *b = _ui._buttonBox->addButton( link._label, QDialogButtonBox::AcceptRole); - b->setDefault(link._isPrimary); + if( activity._links.isEmpty() ) { + // in case there is no action defined, do a close button. + QPushButton *b = _ui._buttonBox->addButton( QDialogButtonBox::Close ); + b->setDefault(true); connect(b, SIGNAL(clicked()), this, SLOT(slotButtonClicked())); _buttons.append(b); + } else { + foreach( auto link, activity._links ) { + QPushButton *b = _ui._buttonBox->addButton(link._label, QDialogButtonBox::AcceptRole); + b->setDefault(link._isPrimary); + connect(b, SIGNAL(clicked()), this, SLOT(slotButtonClicked())); + _buttons.append(b); + } } } @@ -84,12 +93,24 @@ void NotificationWidget::slotButtonClicked() } // if the button was found, the link must be called + if( index > -1 && _myActivity._links.count() == 0 ) { + // no links, that means it was the close button + // empty link. Just close and remove the widget. + QString doneText = tr("Closing in a few seconds..."); + _ui._timeLabel->setText(doneText); + emit requestCleanupAndBlacklist(_myActivity); + return; + } + if( index > -1 && index < _myActivity._links.count() ) { ActivityLink triggeredLink = _myActivity._links.at(index); _actionLabel = triggeredLink._label; - qDebug() << Q_FUNC_INFO << "Notification Link: "<< triggeredLink._verb << triggeredLink._link; - _progressIndi->startAnimation(); - emit sendNotificationRequest( _accountName, triggeredLink._link, triggeredLink._verb ); + + if( ! triggeredLink._link.isEmpty() ) { + qDebug() << Q_FUNC_INFO << "Notification Link: "<< triggeredLink._verb << triggeredLink._link; + _progressIndi->startAnimation(); + emit sendNotificationRequest( _accountName, triggeredLink._link, triggeredLink._verb ); + } } } } @@ -116,12 +137,6 @@ void NotificationWidget::slotNotificationRequestFinished(int statusCode) //* The second parameter is a time, such as 'selected at 09:58pm' doneText = tr("'%1' selected at %2").arg(_actionLabel).arg(timeStr); - - // start a timer, so that activity widget can remove this widget after a - // certain time. It needs to be done by ActivityWidget because it also - // needs to hide the scrollview if no widget is left any more. - // method readyToClose() checks for the timer value to see if it is expired - _closeTimer.start(); } _ui._timeLabel->setText( doneText ); @@ -129,9 +144,4 @@ void NotificationWidget::slotNotificationRequestFinished(int statusCode) } -bool NotificationWidget::readyToClose() -{ - return _closeTimer.isValid() && _closeTimer.elapsed() > NOTIFICATION_WIDGET_CLOSE_AFTER_MILLISECS; -} - } diff --git a/src/gui/notificationwidget.h b/src/gui/notificationwidget.h index c0b87558ab..6f19284873 100644 --- a/src/gui/notificationwidget.h +++ b/src/gui/notificationwidget.h @@ -37,6 +37,7 @@ public: signals: void sendNotificationRequest( const QString&, const QString& link, const QString& verb); + void requestCleanupAndBlacklist( const Activity& activity ); public slots: void setActivity(const Activity& activity); @@ -52,7 +53,6 @@ private: QString _accountName; QProgressIndicator *_progressIndi; QString _actionLabel; - QElapsedTimer _closeTimer; }; }