Merge pull request #4441 from nextcloud/bugfix/activities-fetch-overload

Bugfix/activities fetch server overload
This commit is contained in:
Matthieu Gallien 2022-04-29 23:12:57 +02:00 committed by GitHub
commit a86e0908b7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 145 additions and 41 deletions

View file

@ -43,7 +43,7 @@ void FileActivityListModel::startFetchJob()
if (!accountState()->isConnected()) { if (!accountState()->isConnected()) {
return; return;
} }
setCurrentlyFetching(true); setAndRefreshCurrentlyFetching(true);
const QString url(QStringLiteral("ocs/v2.php/apps/activity/api/v2/activity/filter")); const QString url(QStringLiteral("ocs/v2.php/apps/activity/api/v2/activity/filter"));
auto job = new JsonApiJob(accountState()->account(), url, this); auto job = new JsonApiJob(accountState()->account(), url, this);

View file

@ -34,6 +34,7 @@
#include "activitydata.h" #include "activitydata.h"
#include "activitylistmodel.h" #include "activitylistmodel.h"
#include "systray.h" #include "systray.h"
#include "tray/usermodel.h"
#include "theme.h" #include "theme.h"
@ -95,9 +96,13 @@ void ActivityListModel::setCurrentItem(const int currentItem)
_currentItem = currentItem; _currentItem = currentItem;
} }
void ActivityListModel::setCurrentlyFetching(bool value) void ActivityListModel::setAndRefreshCurrentlyFetching(bool value)
{ {
if (_currentlyFetching == value) {
return;
}
_currentlyFetching = value; _currentlyFetching = value;
insertOrRemoveDummyFetchingActivity();
} }
bool ActivityListModel::currentlyFetching() const bool ActivityListModel::currentlyFetching() const
@ -353,9 +358,9 @@ int ActivityListModel::rowCount(const QModelIndex &parent) const
bool ActivityListModel::canFetchMore(const QModelIndex &) const bool ActivityListModel::canFetchMore(const QModelIndex &) const
{ {
// We need to be connected to be able to fetch more // 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 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; return true;
} }
} }
@ -365,7 +370,7 @@ bool ActivityListModel::canFetchMore(const QModelIndex &) const
void ActivityListModel::startFetchJob() void ActivityListModel::startFetchJob()
{ {
if (!_accountState->isConnected()) { if (!_accountState->isConnected() || currentlyFetching()) {
return; return;
} }
auto *job = new JsonApiJob(_accountState->account(), QLatin1String("ocs/v2.php/apps/activity/api/v2/activity"), this); 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)); params.addQueryItem(QLatin1String("limit"), QString::number(50));
job->addQueryParams(params); job->addQueryParams(params);
_currentlyFetching = true; setAndRefreshCurrentlyFetching(true);
qCInfo(lcActivity) << "Start fetching activities for " << _accountState->account()->displayName(); qCInfo(lcActivity) << "Start fetching activities for " << _accountState->account()->displayName();
job->start(); job->start();
} }
@ -403,7 +408,7 @@ void ActivityListModel::ingestActivities(const QJsonArray &activities)
ActivityList list; ActivityList list;
QDateTime oldestDate = QDateTime::currentDateTime(); QDateTime oldestDate = QDateTime::currentDateTime();
oldestDate = oldestDate.addDays(_maxActivitiesDays * -1); oldestDate = oldestDate.addDays(static_cast<qint64>(_maxActivitiesDays) * -1);
for (const auto &activ : activities) { for (const auto &activ : activities) {
const auto json = activ.toObject(); const auto json = activ.toObject();
@ -423,6 +428,86 @@ void ActivityListModel::ingestActivities(const QJsonArray &activities)
} }
_activityLists.append(list); _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) void ActivityListModel::activitiesReceived(const QJsonDocument &json, int statusCode)
@ -437,12 +522,10 @@ void ActivityListModel::activitiesReceived(const QJsonDocument &json, int status
_doneFetching = true; _doneFetching = true;
} }
_currentlyFetching = false; setAndRefreshCurrentlyFetching(false);
ingestActivities(activities); ingestActivities(activities);
combineActivityLists();
emit activityJobStatusCode(statusCode); emit activityJobStatusCode(statusCode);
} }
@ -513,8 +596,15 @@ void ActivityListModel::removeActivityFromActivityList(Activity activity)
int index = -1; int index = -1;
if (activity._type == Activity::ActivityType) { if (activity._type == Activity::ActivityType) {
index = _activityLists.indexOf(activity); index = _activityLists.indexOf(activity);
if (index != -1) if (index != -1) {
_activityLists.removeAt(index); _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) { } else if (activity._type == Activity::NotificationType) {
index = _notificationLists.indexOf(activity); index = _notificationLists.indexOf(activity);
if (index != -1) if (index != -1)
@ -744,27 +834,21 @@ void ActivityListModel::combineActivityLists()
if (_activityLists.count() > 0) { if (_activityLists.count() > 0) {
std::sort(_activityLists.begin(), _activityLists.end()); std::sort(_activityLists.begin(), _activityLists.end());
resultList.append(_activityLists); 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(); if (_finalList.isEmpty() && !resultList.isEmpty()) {
_finalList = resultList; beginInsertRows({}, 0, resultList.size() - 1);
endResetModel(); _finalList = resultList;
endInsertRows();
} else if (!_finalList.isEmpty()) {
beginResetModel();
_finalList = resultList;
endResetModel();
}
if (_activityLists.size() > 0) {
appendMoreActivitiesAvailableEntry();
}
} }
bool ActivityListModel::canFetchActivities() const bool ActivityListModel::canFetchActivities() const
@ -774,14 +858,14 @@ bool ActivityListModel::canFetchActivities() const
void ActivityListModel::fetchMore(const QModelIndex &) void ActivityListModel::fetchMore(const QModelIndex &)
{ {
if (canFetchActivities() && !_currentlyFetching) { if (canFetchActivities()) {
startFetchJob(); startFetchJob();
} }
} }
void ActivityListModel::slotRefreshActivity() void ActivityListModel::slotRefreshActivity()
{ {
_activityLists.clear(); clearActivities();
_doneFetching = false; _doneFetching = false;
_currentItem = 0; _currentItem = 0;
_totalActivitiesFetched = 0; _totalActivitiesFetched = 0;
@ -795,11 +879,18 @@ void ActivityListModel::slotRefreshActivity()
} }
} }
void ActivityListModel::slotRefreshActivityInitial()
{
if (_activityLists.isEmpty() && !currentlyFetching()) {
slotRefreshActivity();
}
}
void ActivityListModel::slotRemoveAccount() void ActivityListModel::slotRemoveAccount()
{ {
_finalList.clear(); _finalList.clear();
_activityLists.clear(); _activityLists.clear();
_currentlyFetching = false; setAndRefreshCurrentlyFetching(false);
_doneFetching = false; _doneFetching = false;
_currentItem = 0; _currentItem = 0;
_totalActivitiesFetched = 0; _totalActivitiesFetched = 0;

View file

@ -111,6 +111,7 @@ public:
public slots: public slots:
void slotRefreshActivity(); void slotRefreshActivity();
void slotRefreshActivityInitial();
void slotRemoveAccount(); void slotRemoveAccount();
void slotTriggerDefaultAction(const int activityIndex); void slotTriggerDefaultAction(const int activityIndex);
void slotTriggerAction(const int activityIndex, const int actionIndex); void slotTriggerAction(const int activityIndex, const int actionIndex);
@ -125,7 +126,7 @@ protected:
void activitiesReceived(const QJsonDocument &json, int statusCode); void activitiesReceived(const QJsonDocument &json, int statusCode);
QHash<int, QByteArray> roleNames() const override; QHash<int, QByteArray> roleNames() const override;
void setCurrentlyFetching(bool value); void setAndRefreshCurrentlyFetching(bool value);
bool currentlyFetching() const; bool currentlyFetching() const;
void setDoneFetching(bool value); void setDoneFetching(bool value);
void setHideOldActivities(bool value); void setHideOldActivities(bool value);
@ -147,6 +148,11 @@ private:
bool canFetchActivities() const; bool canFetchActivities() const;
void ingestActivities(const QJsonArray &activities); void ingestActivities(const QJsonArray &activities);
void appendMoreActivitiesAvailableEntry();
void insertOrRemoveDummyFetchingActivity();
void clearActivities();
ActivityList _activityLists; ActivityList _activityLists;
ActivityList _syncFileItemLists; ActivityList _syncFileItemLists;

View file

@ -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"); 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 // We want to serve incoming call dialogs to the user for calls that
if(a._objectType == "call" && a._dateTime.secsTo(QDateTime::currentDateTime()) < 120) { if(a._objectType == "call" && a._dateTime.secsTo(QDateTime::currentDateTime()) < 120) {
callList.append(a); callList.append(a);

View file

@ -221,7 +221,7 @@ bool User::checkPushNotificationsAreReady() const
} }
void User::slotRefreshImmediately() { void User::slotRefreshImmediately() {
if (_account.data() && _account.data()->isConnected()) { if (_account.data() && _account.data()->isConnected() && Systray::instance()->isOpen()) {
slotRefreshActivities(); slotRefreshActivities();
} }
slotRefreshNotifications(); slotRefreshNotifications();
@ -233,6 +233,7 @@ void User::slotRefresh()
if (checkPushNotificationsAreReady()) { if (checkPushNotificationsAreReady()) {
// we are relying on WebSocket push notifications - ignore refresh attempts from UI // we are relying on WebSocket push notifications - ignore refresh attempts from UI
slotRefreshActivitiesInitial();
_timeSinceLastCheck[_account.data()].invalidate(); _timeSinceLastCheck[_account.data()].invalidate();
return; return;
} }
@ -249,17 +250,24 @@ void User::slotRefresh()
return; return;
} }
if (_account.data() && _account.data()->isConnected()) { if (_account.data() && _account.data()->isConnected()) {
if (!timer.isValid()) { slotRefreshActivitiesInitial();
slotRefreshActivities();
}
slotRefreshNotifications(); slotRefreshNotifications();
timer.start(); timer.start();
} }
} }
void User::slotRefreshActivitiesInitial()
{
if (_account.data()->isConnected() && Systray::instance()->isOpen()) {
_activityModel->slotRefreshActivityInitial();
}
}
void User::slotRefreshActivities() void User::slotRefreshActivities()
{ {
_activityModel->slotRefreshActivity(); if (_account.data()->isConnected() && Systray::instance()->isOpen()) {
_activityModel->slotRefreshActivity();
}
} }
void User::slotRefreshUserStatus() void User::slotRefreshUserStatus()

View file

@ -102,6 +102,7 @@ public slots:
void slotBuildNotificationDisplay(const ActivityList &list); void slotBuildNotificationDisplay(const ActivityList &list);
void slotBuildIncomingCallDialogs(const ActivityList &list); void slotBuildIncomingCallDialogs(const ActivityList &list);
void slotRefreshNotifications(); void slotRefreshNotifications();
void slotRefreshActivitiesInitial();
void slotRefreshActivities(); void slotRefreshActivities();
void slotRefresh(); void slotRefresh();
void slotRefreshUserStatus(); void slotRefreshUserStatus();