From 7b441a0f6ea08d19d30b06b9b64d8c56fac45b48 Mon Sep 17 00:00:00 2001 From: alex-z Date: Wed, 13 Apr 2022 16:35:40 +0300 Subject: [PATCH] Optimize activities fetch requests. Do not fetch when the Tray is not open. Fetch next subset only when scrolling. Fix duplicate notifications. Signed-off-by: alex-z --- src/gui/fileactivitylistmodel.cpp | 2 +- src/gui/tray/activitylistmodel.cpp | 155 +++++++++++++++++++++------ src/gui/tray/activitylistmodel.h | 8 +- src/gui/tray/notificationhandler.cpp | 2 - src/gui/tray/usermodel.cpp | 18 +++- src/gui/tray/usermodel.h | 1 + 6 files changed, 145 insertions(+), 41 deletions(-) diff --git a/src/gui/fileactivitylistmodel.cpp b/src/gui/fileactivitylistmodel.cpp index ee7360ef2..5fadec981 100644 --- a/src/gui/fileactivitylistmodel.cpp +++ b/src/gui/fileactivitylistmodel.cpp @@ -43,7 +43,7 @@ void FileActivityListModel::startFetchJob() if (!accountState()->isConnected()) { return; } - setCurrentlyFetching(true); + setAndRefreshCurrentlyFetching(true); const QString url(QStringLiteral("ocs/v2.php/apps/activity/api/v2/activity/filter")); auto job = new JsonApiJob(accountState()->account(), url, this); diff --git a/src/gui/tray/activitylistmodel.cpp b/src/gui/tray/activitylistmodel.cpp index d01bb1c7e..147824582 100644 --- a/src/gui/tray/activitylistmodel.cpp +++ b/src/gui/tray/activitylistmodel.cpp @@ -34,6 +34,7 @@ #include "activitydata.h" #include "activitylistmodel.h" #include "systray.h" +#include "tray/usermodel.h" #include "theme.h" @@ -95,9 +96,13 @@ void ActivityListModel::setCurrentItem(const int currentItem) _currentItem = currentItem; } -void ActivityListModel::setCurrentlyFetching(bool value) +void ActivityListModel::setAndRefreshCurrentlyFetching(bool value) { + if (_currentlyFetching == value) { + return; + } _currentlyFetching = value; + insertOrRemoveDummyFetchingActivity(); } bool ActivityListModel::currentlyFetching() const @@ -353,9 +358,9 @@ int ActivityListModel::rowCount(const QModelIndex &parent) const bool ActivityListModel::canFetchMore(const QModelIndex &) const { // We need to be connected to be able to fetch more - if (_accountState && _accountState->isConnected()) { + if (_accountState && _accountState->isConnected() && Systray::instance()->isOpen()) { // If the fetching is reported to be done or we are currently fetching we can't fetch more - if (!_doneFetching && !_currentlyFetching) { + if (!_doneFetching && !currentlyFetching()) { return true; } } @@ -365,7 +370,7 @@ bool ActivityListModel::canFetchMore(const QModelIndex &) const void ActivityListModel::startFetchJob() { - if (!_accountState->isConnected()) { + if (!_accountState->isConnected() || currentlyFetching()) { return; } auto *job = new JsonApiJob(_accountState->account(), QLatin1String("ocs/v2.php/apps/activity/api/v2/activity"), this); @@ -378,7 +383,7 @@ void ActivityListModel::startFetchJob() params.addQueryItem(QLatin1String("limit"), QString::number(50)); job->addQueryParams(params); - _currentlyFetching = true; + setAndRefreshCurrentlyFetching(true); qCInfo(lcActivity) << "Start fetching activities for " << _accountState->account()->displayName(); job->start(); } @@ -403,7 +408,7 @@ void ActivityListModel::ingestActivities(const QJsonArray &activities) ActivityList list; QDateTime oldestDate = QDateTime::currentDateTime(); - oldestDate = oldestDate.addDays(_maxActivitiesDays * -1); + oldestDate = oldestDate.addDays(static_cast(_maxActivitiesDays) * -1); for (const auto &activ : activities) { const auto json = activ.toObject(); @@ -423,6 +428,86 @@ void ActivityListModel::ingestActivities(const QJsonArray &activities) } _activityLists.append(list); + + if (list.size() > 0) { + std::sort(list.begin(), list.end()); + beginInsertRows({}, _finalList.size(), _finalList.size() + list.size() - 1); + _finalList.append(list); + endInsertRows(); + + appendMoreActivitiesAvailableEntry(); + } +} + +void ActivityListModel::appendMoreActivitiesAvailableEntry() +{ + const QString moreActivitiesEntryObjectType = QLatin1String("activity_fetch_more_activities"); + if (_showMoreActivitiesAvailableEntry && !_finalList.isEmpty() + && _finalList.last()._objectType != moreActivitiesEntryObjectType) { + Activity a; + a._type = Activity::ActivityType; + a._accName = _accountState->account()->displayName(); + a._id = -1; + a._objectType = moreActivitiesEntryObjectType; + a._subject = tr("For more activities please open the Activity app."); + a._dateTime = QDateTime::currentDateTime(); + + if (const auto *app = _accountState->findApp(QLatin1String("activity"))) { + a._link = app->url(); + } + + beginInsertRows({}, _finalList.size(), _finalList.size()); + _finalList.append(a); + endInsertRows(); + } +} + +void ActivityListModel::insertOrRemoveDummyFetchingActivity() +{ + const QString dummyFetchingActivityObjectType = QLatin1String("dummy_fetching_activity"); + if (_currentlyFetching && _finalList.isEmpty()) { + Activity a; + a._type = Activity::ActivityType; + a._accName = _accountState->account()->displayName(); + a._id = -2; + a._objectType = dummyFetchingActivityObjectType; + a._subject = tr("Fetching activities..."); + a._dateTime = QDateTime::currentDateTime(); + a._darkIcon = QLatin1String("qrc:///client/theme/colored/change-bordered.svg"); + a._lightIcon = QLatin1String("qrc:///client/theme/colored/change-bordered.svg"); + + beginInsertRows({}, 0, 0); + _finalList.prepend(a); + endInsertRows(); + } else if (!_finalList.isEmpty() && _finalList.first()._objectType == dummyFetchingActivityObjectType) { + beginRemoveRows({}, 0, 0); + _finalList.removeAt(0); + endRemoveRows(); + } +} + +void ActivityListModel::clearActivities() +{ + _activityLists.clear(); + if (!_finalList.isEmpty()) { + const auto firstActivityIt = std::find_if(std::begin(_finalList), std::end(_finalList), + [&](const Activity &activity) { return activity._type == Activity::ActivityType; }); + + if (firstActivityIt != std::end(_finalList)) { + const auto lastActivityItReverse = std::find_if(std::rbegin(_finalList), std::rend(_finalList), + [&](const Activity &activity) { return activity._type == Activity::ActivityType; }); + + const auto lastActivityIt = (lastActivityItReverse + 1).base(); + + if (lastActivityIt != std::end(_finalList)) { + const int beginRemoveIndex = std::distance(std::begin(_finalList), firstActivityIt); + const int endRemoveIndex = std::distance(std::begin(_finalList), lastActivityIt); + beginRemoveRows({}, beginRemoveIndex, endRemoveIndex); + _finalList.erase(firstActivityIt, std::end(_finalList)); + endRemoveRows(); + } + } + } } void ActivityListModel::activitiesReceived(const QJsonDocument &json, int statusCode) @@ -437,12 +522,10 @@ void ActivityListModel::activitiesReceived(const QJsonDocument &json, int status _doneFetching = true; } - _currentlyFetching = false; + setAndRefreshCurrentlyFetching(false); ingestActivities(activities); - combineActivityLists(); - emit activityJobStatusCode(statusCode); } @@ -513,8 +596,15 @@ void ActivityListModel::removeActivityFromActivityList(Activity activity) int index = -1; if (activity._type == Activity::ActivityType) { index = _activityLists.indexOf(activity); - if (index != -1) + if (index != -1) { _activityLists.removeAt(index); + const auto indexInFinalList = _finalList.indexOf(activity); + if (indexInFinalList != -1) { + beginRemoveRows({}, indexInFinalList, indexInFinalList); + _finalList.removeAt(indexInFinalList); + endRemoveRows(); + } + } } else if (activity._type == Activity::NotificationType) { index = _notificationLists.indexOf(activity); if (index != -1) @@ -744,27 +834,21 @@ void ActivityListModel::combineActivityLists() if (_activityLists.count() > 0) { std::sort(_activityLists.begin(), _activityLists.end()); resultList.append(_activityLists); - - if(_showMoreActivitiesAvailableEntry) { - Activity a; - a._type = Activity::ActivityType; - a._accName = _accountState->account()->displayName(); - a._id = -1; - a._subject = tr("For more activities please open the Activity app."); - a._dateTime = QDateTime::currentDateTime(); - - AccountApp *app = _accountState->findApp(QLatin1String("activity")); - if(app) { - a._link = app->url(); - } - - resultList.append(a); - } } - beginResetModel(); - _finalList = resultList; - endResetModel(); + if (_finalList.isEmpty() && !resultList.isEmpty()) { + beginInsertRows({}, 0, resultList.size() - 1); + _finalList = resultList; + endInsertRows(); + } else if (!_finalList.isEmpty()) { + beginResetModel(); + _finalList = resultList; + endResetModel(); + } + + if (_activityLists.size() > 0) { + appendMoreActivitiesAvailableEntry(); + } } bool ActivityListModel::canFetchActivities() const @@ -774,14 +858,14 @@ bool ActivityListModel::canFetchActivities() const void ActivityListModel::fetchMore(const QModelIndex &) { - if (canFetchActivities() && !_currentlyFetching) { + if (canFetchActivities()) { startFetchJob(); } } void ActivityListModel::slotRefreshActivity() { - _activityLists.clear(); + clearActivities(); _doneFetching = false; _currentItem = 0; _totalActivitiesFetched = 0; @@ -795,11 +879,18 @@ void ActivityListModel::slotRefreshActivity() } } +void ActivityListModel::slotRefreshActivityInitial() +{ + if (_activityLists.isEmpty() && !currentlyFetching()) { + slotRefreshActivity(); + } +} + void ActivityListModel::slotRemoveAccount() { _finalList.clear(); _activityLists.clear(); - _currentlyFetching = false; + setAndRefreshCurrentlyFetching(false); _doneFetching = false; _currentItem = 0; _totalActivitiesFetched = 0; diff --git a/src/gui/tray/activitylistmodel.h b/src/gui/tray/activitylistmodel.h index 43cc211b5..8ea3a49a1 100644 --- a/src/gui/tray/activitylistmodel.h +++ b/src/gui/tray/activitylistmodel.h @@ -111,6 +111,7 @@ public: public slots: void slotRefreshActivity(); + void slotRefreshActivityInitial(); void slotRemoveAccount(); void slotTriggerDefaultAction(const int activityIndex); void slotTriggerAction(const int activityIndex, const int actionIndex); @@ -125,7 +126,7 @@ protected: void activitiesReceived(const QJsonDocument &json, int statusCode); QHash roleNames() const override; - void setCurrentlyFetching(bool value); + void setAndRefreshCurrentlyFetching(bool value); bool currentlyFetching() const; void setDoneFetching(bool value); void setHideOldActivities(bool value); @@ -147,6 +148,11 @@ private: bool canFetchActivities() const; void ingestActivities(const QJsonArray &activities); + void appendMoreActivitiesAvailableEntry(); + + void insertOrRemoveDummyFetchingActivity(); + + void clearActivities(); ActivityList _activityLists; ActivityList _syncFileItemLists; diff --git a/src/gui/tray/notificationhandler.cpp b/src/gui/tray/notificationhandler.cpp index f274b80b9..d21f0d7c6 100644 --- a/src/gui/tray/notificationhandler.cpp +++ b/src/gui/tray/notificationhandler.cpp @@ -137,8 +137,6 @@ void ServerNotificationHandler::slotNotificationsReceived(const QJsonDocument &j a._talkNotificationData.userAvatar = ai->account()->url().toString() + QStringLiteral("/index.php/avatar/") + a._subjectRichParameters["user"].id + QStringLiteral("/128"); } - list.append(a); - // We want to serve incoming call dialogs to the user for calls that if(a._objectType == "call" && a._dateTime.secsTo(QDateTime::currentDateTime()) < 120) { callList.append(a); diff --git a/src/gui/tray/usermodel.cpp b/src/gui/tray/usermodel.cpp index 7bc10a1fb..d7a817748 100644 --- a/src/gui/tray/usermodel.cpp +++ b/src/gui/tray/usermodel.cpp @@ -221,7 +221,7 @@ bool User::checkPushNotificationsAreReady() const } void User::slotRefreshImmediately() { - if (_account.data() && _account.data()->isConnected()) { + if (_account.data() && _account.data()->isConnected() && Systray::instance()->isOpen()) { slotRefreshActivities(); } slotRefreshNotifications(); @@ -233,6 +233,7 @@ void User::slotRefresh() if (checkPushNotificationsAreReady()) { // we are relying on WebSocket push notifications - ignore refresh attempts from UI + slotRefreshActivitiesInitial(); _timeSinceLastCheck[_account.data()].invalidate(); return; } @@ -249,17 +250,24 @@ void User::slotRefresh() return; } if (_account.data() && _account.data()->isConnected()) { - if (!timer.isValid()) { - slotRefreshActivities(); - } + slotRefreshActivitiesInitial(); slotRefreshNotifications(); timer.start(); } } +void User::slotRefreshActivitiesInitial() +{ + if (_account.data()->isConnected() && Systray::instance()->isOpen()) { + _activityModel->slotRefreshActivityInitial(); + } +} + void User::slotRefreshActivities() { - _activityModel->slotRefreshActivity(); + if (_account.data()->isConnected() && Systray::instance()->isOpen()) { + _activityModel->slotRefreshActivity(); + } } void User::slotRefreshUserStatus() diff --git a/src/gui/tray/usermodel.h b/src/gui/tray/usermodel.h index c69e7827b..10c9cd1a9 100644 --- a/src/gui/tray/usermodel.h +++ b/src/gui/tray/usermodel.h @@ -102,6 +102,7 @@ public slots: void slotBuildNotificationDisplay(const ActivityList &list); void slotBuildIncomingCallDialogs(const ActivityList &list); void slotRefreshNotifications(); + void slotRefreshActivitiesInitial(); void slotRefreshActivities(); void slotRefresh(); void slotRefreshUserStatus();