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.
This commit is contained in:
Klaas Freitag 2016-03-23 16:59:03 +01:00
parent 0c944a06f9
commit 1fe5d6bb0c
4 changed files with 103 additions and 37 deletions

View File

@ -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<int> 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<QDateTime, NotificationWidget*> 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);

View File

@ -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<QString> _accountsWithoutActivities;
QMap<int, NotificationWidget*> _widgetForNotifId;
QMap<Activity::Identifier, NotificationWidget*> _widgetForNotifId;
QElapsedTimer _guiLogTimer;
QSet<int> _guiLoggedNotifications;
ActivityList _blacklistedActivities;
QSet< QPair<QDateTime, NotificationWidget*> > _widgetsToRemove;
QTimer _removeTimer;
// number of currently running notification requests. If non zero,
// no query for notifications is started.

View File

@ -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;
}
}

View File

@ -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;
};
}