Skip to content

Commit 26e8d6f

Browse files
authored
Merge pull request #8782 from nextcloud/bugfix/6624/duplicate-notifications-in-activity-list
fix: try to avoid displaying duplicate server notifications in activity list
2 parents 40659ca + b9f1467 commit 26e8d6f

File tree

9 files changed

+134
-69
lines changed

9 files changed

+134
-69
lines changed

src/gui/systray.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ private slots:
187187

188188
AccessManagerFactory _accessManagerFactory;
189189

190-
QSet<qlonglong> _callsAlreadyNotified;
190+
QSet<qint64> _callsAlreadyNotified;
191191
QPointer<QObject> _editFileLocallyLoadingDialog;
192192
QPointer<QObject> _encryptionTokenDiscoveryDialog;
193193
QVector<QQuickWindow*> _fileDetailDialogs;

src/gui/tray/activitydata.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ OCC::Activity Activity::fromActivityJson(const QJsonObject &json, const AccountP
7777
activity._objectType = json.value(QStringLiteral("object_type")).toString();
7878
activity._objectId = json.value(QStringLiteral("object_id")).toInt();
7979
activity._objectName = json.value(QStringLiteral("object_name")).toString();
80-
activity._id = json.value(QStringLiteral("activity_id")).toInt();
80+
activity._id = json.value(QStringLiteral("activity_id")).toInteger();
8181
activity._fileAction = json.value(QStringLiteral("type")).toString();
8282
activity._accName = account->displayName();
8383
activity._subject = json.value(QStringLiteral("subject")).toString();

src/gui/tray/activitydata.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ class Activity
136136
Q_PROPERTY(QVariantMap subjectRichParameters MEMBER _subjectRichParameters)
137137

138138
public:
139-
using Identifier = QPair<qlonglong, QString>;
139+
using Identifier = QPair<qint64, QString>;
140140

141141
// Note that these are in the order we want to present them in the model!
142142
enum Type {
@@ -160,7 +160,7 @@ class Activity
160160
using TalkNotificationData = OCC::TalkNotificationData;
161161

162162
Type _type;
163-
qlonglong _id = 0LL;
163+
qint64 _id = 0LL;
164164
QString _fileAction;
165165
int _objectId = 0;
166166
TalkNotificationData _talkNotificationData;

src/gui/tray/activitylistmodel.cpp

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -523,18 +523,37 @@ void ActivityListModel::activitiesReceived(const QJsonDocument &json, int status
523523

524524
void ActivityListModel::addEntriesToActivityList(const ActivityList &activityList)
525525
{
526-
if(activityList.isEmpty()) {
526+
if (activityList.isEmpty()) {
527+
return;
528+
}
529+
530+
// filter activities that do not need to be added again
531+
ActivityList filteredList;
532+
std::copy_if(activityList.constBegin(), activityList.constEnd(), std::back_inserter(filteredList), [this](const auto &activity) -> bool {
533+
if (activity._type == Activity::NotificationType && _activeNotificationIds.contains(activity._id)) {
534+
return false;
535+
}
536+
return true;
537+
});
538+
539+
if (filteredList.isEmpty()) {
527540
return;
528541
}
529542

530543
const auto startRow = _finalList.count();
531544

532-
beginInsertRows({}, startRow, startRow + activityList.count() - 1);
533-
for(const auto &activity : activityList) {
545+
beginInsertRows({}, startRow, startRow + filteredList.count() - 1);
546+
for(const auto &activity : std::as_const(filteredList)) {
534547
_finalList.append(activity);
548+
535549
if (activity._syncFileItemStatus == SyncFileItem::Conflict) {
536550
_conflictsList.push_back(activity);
537551
}
552+
553+
if (activity._type == Activity::NotificationType) {
554+
// new unseen notification, to avoid duplicate activities to appear add its id to the filter list
555+
_activeNotificationIds.insert(activity._id);
556+
}
538557
}
539558
endInsertRows();
540559

@@ -591,7 +610,6 @@ void ActivityListModel::addNotificationToActivityList(const Activity &activity)
591610
{
592611
qCDebug(lcActivity) << "Notification successfully added to the notification list: " << activity._subject;
593612
addEntriesToActivityList({activity});
594-
_notificationLists.prepend(activity);
595613
for (const auto &link : activity._links) {
596614
if (link._verb == QByteArrayLiteral("POST")
597615
|| link._verb == QByteArrayLiteral("REPLY")
@@ -605,7 +623,6 @@ void ActivityListModel::addSyncFileItemToActivityList(const Activity &activity)
605623
{
606624
qCDebug(lcActivity) << "Successfully added to the activity list: " << activity._subject;
607625
addEntriesToActivityList({activity});
608-
_syncFileItemLists.prepend(activity);
609626
}
610627

611628
void ActivityListModel::removeActivityFromActivityList(int row)
@@ -627,27 +644,37 @@ void ActivityListModel::removeActivityFromActivityList(const Activity &activity)
627644
_conflictsList.removeOne(activity);
628645
}
629646

647+
if (activity._type == Activity::NotificationType) {
648+
_activeNotificationIds.remove(activity._id);
649+
}
650+
630651
if (activity._type != Activity::ActivityType &&
631652
activity._type != Activity::DummyFetchingActivityType &&
632653
activity._type != Activity::DummyMoreActivitiesAvailableType &&
633654
activity._type != Activity::NotificationType &&
634655
activity._type != Activity::OpenSettingsNotificationType) {
635656

636657
const auto notificationErrorsListIndex = _notificationErrorsLists.indexOf(activity);
637-
if (notificationErrorsListIndex != -1)
658+
if (notificationErrorsListIndex != -1) {
638659
_notificationErrorsLists.removeAt(notificationErrorsListIndex);
660+
}
639661
}
640662
}
641663

642-
void ActivityListModel::checkAndRemoveSeenActivities(const OCC::ActivityList &newActivities)
664+
void ActivityListModel::removeOutdatedNotifications(const OCC::ActivityList &receivedNotifications)
643665
{
644666
ActivityList activitiesToRemove;
645667
for (const auto &activity : _finalList) {
646-
const auto isTalkActiity = activity._objectType == QStringLiteral("chat") ||
647-
activity._objectType == QStringLiteral("call");
648-
if (isTalkActiity && !newActivities.contains(activity)) {
649-
activitiesToRemove.push_back(activity);
668+
if (activity._type != Activity::NotificationType || receivedNotifications.contains(activity)) {
669+
continue;
650670
}
671+
672+
qCDebug(lcActivity).nospace() << "marking notification activity for removal"
673+
<< " activity.type=" << activity._type
674+
<< " activity.id=" << activity._id
675+
<< " activity.objectType=" << activity._objectType
676+
<< " activity.accName=" << activity._accName;
677+
activitiesToRemove.push_back(activity);
651678
}
652679

653680
for (const auto &toRemove : activitiesToRemove) {
@@ -930,6 +957,7 @@ void ActivityListModel::slotRemoveAccount()
930957
_conflictsList.clear();
931958
_activityLists.clear();
932959
_presentedActivities.clear();
960+
_activeNotificationIds.clear();
933961
setAndRefreshCurrentlyFetching(false);
934962
_doneFetching = false;
935963
_currentItem = 0;

src/gui/tray/activitylistmodel.h

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ public slots:
127127
void removeActivityFromActivityList(int row);
128128
void removeActivityFromActivityList(const OCC::Activity &activity);
129129

130-
void checkAndRemoveSeenActivities(const OCC::ActivityList &newActivities);
130+
void removeOutdatedNotifications(const OCC::ActivityList &receivedNotifications);
131131

132132
void setAccountState(OCC::AccountState *state);
133133
void setReplyMessageSent(const int activityIndex, const QString &message);
@@ -179,18 +179,16 @@ private slots:
179179
Activity _dummyFetchingActivities;
180180

181181
ActivityList _activityLists;
182-
ActivityList _syncFileItemLists;
183-
ActivityList _notificationLists;
184-
ActivityList _listOfIgnoredFiles;
185182
ActivityList _notificationErrorsLists;
186183
ActivityList _conflictsList;
187184
ActivityList _finalList;
188185

189186
QSet<qint64> _presentedActivities;
187+
QSet<qint64> _activeNotificationIds;
190188

191189
bool _displayActions = true;
192190

193-
int _currentItem = 0;
191+
qint64 _currentItem = 0;
194192
static constexpr int _maxActivities = 100;
195193
static constexpr int _maxActivitiesDays = 30;
196194
bool _showMoreActivitiesAvailableEntry = false;

src/gui/tray/notificationhandler.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ void ServerNotificationHandler::slotNotificationsReceived(const QJsonDocument &j
106106
auto a = Activity::fromActivityJson(json, ai->account());
107107

108108
a._type = Activity::NotificationType;
109-
a._id = json.value("notification_id").toInt();
109+
a._id = json.value("notification_id").toInteger();
110110

111111
if(json.contains("subjectRichParameters")) {
112112
const auto richParams = json.value("subjectRichParameters").toObject();

src/gui/tray/usermodel.cpp

Lines changed: 33 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -125,38 +125,30 @@ User::User(AccountStatePtr &account, const bool &isCurrent, QObject *parent)
125125

126126
void User::checkNotifiedNotifications()
127127
{
128-
// after one hour, clear the gui log notification store
128+
// clear the gui log notification store after one hour has passed since the last received notification
129129
constexpr qint64 clearGuiLogInterval = 60 * 60 * 1000;
130130
if (_guiLogTimer.elapsed() > clearGuiLogInterval) {
131131
_notifiedNotifications.clear();
132132
}
133133
}
134134

135-
bool User::notificationAlreadyShown(const long notificationId)
135+
bool User::notificationAlreadyShown(const qint64 notificationId)
136136
{
137137
checkNotifiedNotifications();
138138
return _notifiedNotifications.contains(notificationId);
139139
}
140140

141-
bool User::canShowNotification(const long notificationId)
141+
bool User::canShowNotification(const qint64 notificationId)
142142
{
143143
ConfigFile cfg;
144144
return cfg.optionalServerNotifications() &&
145145
isDesktopNotificationsAllowed() &&
146146
!notificationAlreadyShown(notificationId);
147147
}
148148

149-
void User::checkAndRemoveSeenActivities(const ActivityList &list, const int numTalkNotificationsReceived)
149+
void User::showDesktopNotification(const QString &title, const QString &message, const qint64 notificationId)
150150
{
151-
if (numTalkNotificationsReceived < _lastTalkNotificationsReceivedCount) {
152-
_activityModel->checkAndRemoveSeenActivities(list);
153-
}
154-
_lastTalkNotificationsReceivedCount = numTalkNotificationsReceived;
155-
}
156-
157-
void User::showDesktopNotification(const QString &title, const QString &message, const long notificationId)
158-
{
159-
if(!canShowNotification(notificationId)) {
151+
if (!canShowNotification(notificationId)) {
160152
return;
161153
}
162154

@@ -200,7 +192,7 @@ void User::showDesktopNotification(const ActivityList &activityList)
200192

201193
Logger::instance()->postGuiLog(subject, message);
202194

203-
for(const auto &activity : activityList) {
195+
for (const auto &activity : activityList) {
204196
_notifiedNotifications.insert(activity._id);
205197
_activityModel->addNotificationToActivityList(activity);
206198
}
@@ -232,43 +224,41 @@ void User::showDesktopTalkNotification(const Activity &activity)
232224

233225
void User::slotBuildNotificationDisplay(const ActivityList &list)
234226
{
235-
const auto talkNotificationsReceivedCount = std::count_if(std::cbegin(list), std::cend(list), [](const auto &activity) {
236-
return activity._objectType == QStringLiteral("chat") ||
237-
activity._objectType == QStringLiteral("call");
238-
});
239-
checkAndRemoveSeenActivities(list, talkNotificationsReceivedCount);
240-
241227
ActivityList toNotifyList;
242228

243-
std::copy_if(list.constBegin(), list.constEnd(), std::back_inserter(toNotifyList), [&](const Activity &activity) {
229+
_activityModel->removeOutdatedNotifications(list);
244230

245-
if (_blacklistedNotifications.contains(activity)) {
246-
qCInfo(lcActivity) << "Activity in blacklist, skip";
247-
return false;
248-
} else if(_notifiedNotifications.contains(activity._id)) {
249-
qCInfo(lcActivity) << "Activity already notified, skip";
231+
std::copy_if(list.constBegin(), list.constEnd(), std::back_inserter(toNotifyList), [&](const Activity &activity) -> bool {
232+
if (!activity._shouldNotify) {
233+
qCDebug(lcActivity).nospace() << "No notification should be sent for activity with id=" << activity._id << " objectType=" << activity._objectType;
250234
return false;
251235
}
252-
if (!activity._shouldNotify) {
253-
qCDebug(lcActivity) << "Activity should not be notified";
236+
237+
if (_notifiedNotifications.contains(activity._id)) {
238+
qCInfo(lcActivity).nospace() << "Ignoring already notified activity with id=" << activity._id << " objectType=" << activity._objectType;
254239
return false;
255240
}
256241

257242
return true;
258243
});
259244

260-
if(toNotifyList.count() > 2) {
261-
showDesktopNotification(toNotifyList);
245+
if (toNotifyList.isEmpty()) {
262246
return;
263247
}
264248

265-
for (const auto &activity : std::as_const(toNotifyList)) {
249+
if (toNotifyList.size() == 1) {
250+
const auto &activity = toNotifyList.constFirst();
266251
if (activity._objectType == QStringLiteral("chat")) {
252+
// Talk's "call" type is handled in slotBuildIncomingCallDialogs
267253
showDesktopTalkNotification(activity);
268-
} else {
269-
showDesktopNotification(activity);
254+
return;
270255
}
256+
257+
showDesktopNotification(activity);
258+
return;
271259
}
260+
261+
showDesktopNotification(toNotifyList);
272262
}
273263

274264
void User::slotNotificationFetchFinished()
@@ -288,16 +278,18 @@ void User::slotBuildIncomingCallDialogs(const ActivityList &list)
288278
}
289279

290280
const auto systray = Systray::instance();
281+
if (!systray) {
282+
qCWarning(lcActivity) << "No systray instance available, can not notify about new calls";
283+
return;
284+
}
291285

292-
if(systray) {
293-
for(const auto &activity : list) {
294-
if (!activity._shouldNotify) {
295-
qCDebug(lcActivity) << "Activity should not be notified";
296-
continue;
297-
}
298-
299-
systray->createCallDialog(activity, _account);
286+
for (const auto &activity : list) {
287+
if (!activity._shouldNotify) {
288+
qCDebug(lcActivity).nospace() << "No notification should be sent for activity with id=" << activity._id << " objectType=" << activity._objectType;
289+
continue;
300290
}
291+
292+
systray->createCallDialog(activity, _account);
301293
}
302294
}
303295

src/gui/tray/usermodel.h

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ private slots:
164164
void slotGroupFoldersFetched(QNetworkReply *reply);
165165
void slotQuotaChanged(const int64_t &usedBytes, const int64_t &availableBytes);
166166
void checkNotifiedNotifications();
167-
void showDesktopNotification(const QString &title, const QString &message, const long notificationId);
167+
void showDesktopNotification(const QString &title, const QString &message, const qint64 notificationId);
168168
void showDesktopNotification(const OCC::Activity &activity);
169169
void showDesktopNotification(const OCC::ActivityList &activityList);
170170
void showDesktopTalkNotification(const OCC::Activity &activity);
@@ -178,18 +178,15 @@ private slots:
178178
bool isActivityOfCurrentAccount(const Folder *folder) const;
179179
[[nodiscard]] bool isUnsolvableConflict(const SyncFileItemPtr &item) const;
180180

181-
bool notificationAlreadyShown(const long notificationId);
182-
bool canShowNotification(const long notificationId);
183-
184-
void checkAndRemoveSeenActivities(const ActivityList &list, const int numTalkNotificationsReceived);
181+
bool notificationAlreadyShown(const qint64 notificationId);
182+
bool canShowNotification(const qint64 notificationId);
185183

186184
[[nodiscard]] bool serverHasTalk() const;
187185

188186
AccountStatePtr _account;
189187
bool _isCurrentUser;
190188
ActivityListModel *_activityModel;
191189
UnifiedSearchResultsListModel *_unifiedSearchResultsModel;
192-
ActivityList _blacklistedNotifications;
193190

194191
QVariantList _trayFolderInfos;
195192

@@ -198,7 +195,8 @@ private slots:
198195
QHash<AccountState *, QElapsedTimer> _timeSinceLastCheck;
199196

200197
QElapsedTimer _guiLogTimer;
201-
QSet<long> _notifiedNotifications;
198+
QSet<qint64> _notifiedNotifications;
199+
QSet<qint64> _activeNotifications;
202200
QMimeDatabase _mimeDb;
203201

204202
// number of currently running notification requests. If non zero,

0 commit comments

Comments
 (0)