-
Notifications
You must be signed in to change notification settings - Fork 668
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Show Server Notifications on the Client #4567
Merged
Merged
Changes from all commits
Commits
Show all changes
44 commits
Select commit
Hold shift + click to select a range
0eb1041
AbstractNetworkJob: Add a delete job.
a831b74
Added temporar icon for notifications.
eb00b34
Minor wording fixes
688c550
New GUI class NotificationWidget.
32e16b3
Display server notifications on the client (#3733)
4a4dac2
Notifications: Add a Progress indicator and handle job results.
b97c832
Capabilities: Add isValid check and check for notifications
7d13a1d
Notifications: Check capabilities if the notifications are enabled
8a0ce46
Notifications: Properly delete the notification check job.
903e79a
Notifications: Do a GUI tray notification if new notifciations arrive.
2d1ab27
Notifications: Refactor - create a notification handler class
2c2a18a
Activitiy: Refactor - move classes to their own source files.
adf9570
Notification: Enhance the tray message
73cd5a9
Notifications: Cleaner notification string build
97f1694
ActivityData: Simplified implementation.
9d219a1
ActivityListModel: Code cleanups
9a2f145
ocs jobs: Add a define for OCS job success.
a4dcc27
Notification: Fix plural handling for tray message
45c32ec
NotificationWidget: Remove not needed method.
f7f4120
Activity: Some documentation and better varialbe names
f71fdab
Fix timeAgoInWords
05de710
Notifications: Display timestamp of the notification in the widget
0a590b7
Notifications: Give feedback if notifcation request succeeded.
328d254
Notifications: Remove "done" notification widgets after fife seconds.
7f22a07
Notifications: Check if the account is connected before querying.
b966345
Notifications: Refresh the notifications based on a config value.
f587f35
Fix plural translation handling, remove the superflous arg()
d407aac
Notifications: remove notification widgets if the notification is gone.
d03fcc9
Notifications: Maintain a timeSinceLastCheck for every Account.
ad60e8a
Notifications: Fix handling of notifications to remove from the list.
f70c628
Notifications: Remove unused variable.
ea2f19b
Docs: Add new config option for the notification sync interval.
161d219
ActivityData: Add source file for implementation details
1bb3a4a
NotificationWidget: Remove accountName() and add activity() method.
0c944a0
NotificationWidgetUI: Fix sizing and sizePolicy
1fe5d6b
Notifications: Handle Notifications without an action.
69e8e15
Remove explicit time spec specification as it is not needed.
4d59f5e
ActivityData: Declare operators outside the class
cacb751
Cleaups based on review feedback.
9f438cb
Doc: Add milliseconds unit to notificationRefreshInterval doc
2e30a0e
Remove superflous iterator increment
cd3f612
ActivityWidget: Rename blacklistActivities to blacklistNotifications.
8166c52
NotificationHandling: Use QByteArray for the verb.
885f8b3
ActivityWidget: Handle plural properly in translations.
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
/* | ||
* Copyright (C) by Klaas Freitag <[email protected]> | ||
* | ||
* This program is free software; you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License as published by | ||
* the Free Software Foundation; version 2 of the License. | ||
* | ||
* This program is distributed in the hope that it will be useful, but | ||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | ||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
* for more details. | ||
*/ | ||
|
||
#include <QtCore> | ||
|
||
#include "activitydata.h" | ||
|
||
|
||
namespace OCC | ||
{ | ||
|
||
bool operator<( const Activity& rhs, const Activity& lhs ) { | ||
return rhs._dateTime.toMSecsSinceEpoch() > lhs._dateTime.toMSecsSinceEpoch(); | ||
} | ||
|
||
bool operator==( const Activity& rhs, const Activity& lhs ) { | ||
return (rhs._type == lhs._type && rhs._id== lhs._id && rhs._accName == lhs._accName); | ||
} | ||
|
||
Activity::Identifier Activity::ident() const { | ||
return Identifier( _id, _accName ); | ||
} | ||
|
||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
/* | ||
* Copyright (C) by Klaas Freitag <[email protected]> | ||
* | ||
* This program is free software; you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License as published by | ||
* the Free Software Foundation; version 2 of the License. | ||
* | ||
* This program is distributed in the hope that it will be useful, but | ||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | ||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
* for more details. | ||
*/ | ||
|
||
#ifndef ACTIVITYDATA_H | ||
#define ACTIVITYDATA_H | ||
|
||
#include <QtCore> | ||
|
||
namespace OCC { | ||
/** | ||
* @brief The ActivityLink class describes actions of an activity | ||
* | ||
* These are part of notifications which are mapped into activities. | ||
*/ | ||
|
||
class ActivityLink | ||
{ | ||
public: | ||
QString _label; | ||
QString _link; | ||
QByteArray _verb; | ||
bool _isPrimary; | ||
}; | ||
|
||
/* ==================================================================== */ | ||
/** | ||
* @brief Activity Structure | ||
* @ingroup gui | ||
* | ||
* contains all the information describing a single activity. | ||
*/ | ||
|
||
class Activity | ||
{ | ||
public: | ||
typedef QPair<qlonglong, QString> Identifier; | ||
|
||
enum Type { | ||
ActivityType, | ||
NotificationType | ||
}; | ||
|
||
Type _type; | ||
qlonglong _id; | ||
QString _subject; | ||
QString _message; | ||
QString _file; | ||
QUrl _link; | ||
QDateTime _dateTime; | ||
QString _accName; | ||
|
||
QVector <ActivityLink> _links; | ||
/** | ||
* @brief Sort operator to sort the list youngest first. | ||
* @param val | ||
* @return | ||
*/ | ||
|
||
|
||
Identifier ident() const; | ||
}; | ||
|
||
bool operator==( const Activity& rhs, const Activity& lhs ); | ||
bool operator<( const Activity& rhs, const Activity& lhs ); | ||
|
||
/* ==================================================================== */ | ||
/** | ||
* @brief The ActivityList | ||
* @ingroup gui | ||
* | ||
* A QList based list of Activities | ||
*/ | ||
|
||
typedef QList<Activity> ActivityList; | ||
|
||
|
||
} | ||
|
||
#endif // ACTIVITYDATA_H |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,228 @@ | ||
/* | ||
* Copyright (C) by Klaas Freitag <[email protected]> | ||
* | ||
* This program is free software; you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License as published by | ||
* the Free Software Foundation; version 2 of the License. | ||
* | ||
* This program is distributed in the hope that it will be useful, but | ||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | ||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
* for more details. | ||
*/ | ||
|
||
#include <QtCore> | ||
#include <QAbstractListModel> | ||
#include <QWidget> | ||
#include <QIcon> | ||
|
||
#include "account.h" | ||
#include "accountstate.h" | ||
#include "accountmanager.h" | ||
#include "folderman.h" | ||
#include "accessmanager.h" | ||
#include "activityitemdelegate.h" | ||
|
||
#include "activitydata.h" | ||
#include "activitylistmodel.h" | ||
|
||
namespace OCC { | ||
|
||
ActivityListModel::ActivityListModel(QWidget *parent) | ||
:QAbstractListModel(parent) | ||
{ | ||
} | ||
|
||
QVariant ActivityListModel::data(const QModelIndex &index, int role) const | ||
{ | ||
Activity a; | ||
|
||
if (!index.isValid()) | ||
return QVariant(); | ||
|
||
a = _finalList.at(index.row()); | ||
AccountStatePtr ast = AccountManager::instance()->account(a._accName); | ||
QStringList list; | ||
|
||
switch (role) { | ||
case ActivityItemDelegate::PathRole: | ||
list = FolderMan::instance()->findFileInLocalFolders(a._file, ast->account()); | ||
if( list.count() > 0 ) { | ||
return QVariant(list.at(0)); | ||
} | ||
// File does not exist anymore? Let's try to open its path | ||
list = FolderMan::instance()->findFileInLocalFolders(QFileInfo(a._file).path(), ast->account()); | ||
if( list.count() > 0 ) { | ||
return QVariant(list.at(0)); | ||
} | ||
return QVariant(); | ||
break; | ||
case ActivityItemDelegate::ActionIconRole: | ||
return QVariant(); // FIXME once the action can be quantified, display on Icon | ||
break; | ||
case ActivityItemDelegate::UserIconRole: | ||
return QIcon(QLatin1String(":/client/resources/account.png")); | ||
break; | ||
case Qt::ToolTipRole: | ||
case ActivityItemDelegate::ActionTextRole: | ||
return a._subject; | ||
break; | ||
case ActivityItemDelegate::LinkRole: | ||
return a._link; | ||
break; | ||
case ActivityItemDelegate::AccountRole: | ||
return a._accName; | ||
break; | ||
case ActivityItemDelegate::PointInTimeRole: | ||
return Utility::timeAgoInWords(a._dateTime); | ||
break; | ||
case ActivityItemDelegate::AccountConnectedRole: | ||
return (ast && ast->isConnected()); | ||
break; | ||
default: | ||
return QVariant(); | ||
|
||
} | ||
return QVariant(); | ||
|
||
} | ||
|
||
int ActivityListModel::rowCount(const QModelIndex&) const | ||
{ | ||
return _finalList.count(); | ||
} | ||
|
||
// current strategy: Fetch 100 items per Account | ||
// ATTENTION: This method is const and thus it is not possible to modify | ||
// the _activityLists hash or so. Doesn't make it easier... | ||
bool ActivityListModel::canFetchMore(const QModelIndex& ) const | ||
{ | ||
if( _activityLists.count() == 0 ) return true; | ||
|
||
for(auto i = _activityLists.begin() ; i != _activityLists.end(); ++i) { | ||
AccountState *ast = i.key(); | ||
if( ast && ast->isConnected() ) { | ||
ActivityList activities = i.value(); | ||
if( activities.count() == 0 && | ||
! _currentlyFetching.contains(ast) ) { | ||
return true; | ||
} | ||
} | ||
} | ||
|
||
return false; | ||
} | ||
|
||
void ActivityListModel::startFetchJob(AccountState* s) | ||
{ | ||
if( !s->isConnected() ) { | ||
return; | ||
} | ||
JsonApiJob *job = new JsonApiJob(s->account(), QLatin1String("ocs/v1.php/cloud/activity"), this); | ||
QObject::connect(job, SIGNAL(jsonReceived(QVariantMap, int)), | ||
this, SLOT(slotActivitiesReceived(QVariantMap, int))); | ||
job->setProperty("AccountStatePtr", QVariant::fromValue<AccountState*>(s)); | ||
|
||
QList< QPair<QString,QString> > params; | ||
params.append(qMakePair(QString::fromLatin1("page"), QString::fromLatin1("0"))); | ||
params.append(qMakePair(QString::fromLatin1("pagesize"), QString::fromLatin1("100"))); | ||
job->addQueryParams(params); | ||
|
||
_currentlyFetching.insert(s); | ||
qDebug() << Q_FUNC_INFO << "Start fetching activities for " << s->account()->displayName(); | ||
job->start(); | ||
} | ||
|
||
void ActivityListModel::slotActivitiesReceived(const QVariantMap& json, int statusCode) | ||
{ | ||
auto activities = json.value("ocs").toMap().value("data").toList(); | ||
|
||
ActivityList list; | ||
AccountState* ast = qvariant_cast<AccountState*>(sender()->property("AccountStatePtr")); | ||
_currentlyFetching.remove(ast); | ||
|
||
foreach( auto activ, activities ) { | ||
auto json = activ.toMap(); | ||
|
||
Activity a; | ||
a._type = Activity::ActivityType; | ||
a._accName = ast->account()->displayName(); | ||
a._id = json.value("id").toLongLong(); | ||
a._subject = json.value("subject").toString(); | ||
a._message = json.value("message").toString(); | ||
a._file = json.value("file").toString(); | ||
a._link = json.value("link").toUrl(); | ||
a._dateTime = json.value("date").toDateTime(); | ||
list.append(a); | ||
} | ||
|
||
_activityLists[ast] = list; | ||
|
||
emit activityJobStatusCode(ast, statusCode); | ||
|
||
combineActivityLists(); | ||
} | ||
|
||
|
||
void ActivityListModel::combineActivityLists() | ||
{ | ||
ActivityList resultList; | ||
|
||
foreach( ActivityList list, _activityLists.values() ) { | ||
resultList.append(list); | ||
} | ||
|
||
std::sort( resultList.begin(), resultList.end() ); | ||
|
||
beginResetModel(); | ||
_finalList.clear(); | ||
endResetModel(); | ||
|
||
beginInsertRows(QModelIndex(), 0, resultList.count()); | ||
_finalList = resultList; | ||
endInsertRows(); | ||
} | ||
|
||
void ActivityListModel::fetchMore(const QModelIndex &) | ||
{ | ||
QList<AccountStatePtr> accounts = AccountManager::instance()->accounts(); | ||
|
||
foreach (const AccountStatePtr& asp, accounts) { | ||
|
||
if( !_activityLists.contains(asp.data()) && asp->isConnected() ) { | ||
_activityLists[asp.data()] = ActivityList(); | ||
startFetchJob(asp.data()); | ||
} | ||
} | ||
} | ||
|
||
void ActivityListModel::slotRefreshActivity(AccountState *ast) | ||
{ | ||
if(ast && _activityLists.contains(ast)) { | ||
_activityLists.remove(ast); | ||
} | ||
startFetchJob(ast); | ||
} | ||
|
||
void ActivityListModel::slotRemoveAccount(AccountState *ast ) | ||
{ | ||
if( _activityLists.contains(ast) ) { | ||
int i = 0; | ||
const QString accountToRemove = ast->account()->displayName(); | ||
|
||
QMutableListIterator<Activity> it(_finalList); | ||
|
||
while (it.hasNext()) { | ||
Activity activity = it.next(); | ||
if( activity._accName == accountToRemove ) { | ||
beginRemoveRows(QModelIndex(), i, i+1); | ||
it.remove(); | ||
endRemoveRows(); | ||
} | ||
} | ||
_activityLists.remove(ast); | ||
_currentlyFetching.remove(ast); | ||
} | ||
} | ||
|
||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Normaly, fetchMore is too be used when there are more items to fetch that are hidden.
So that can be used in order to fetch older items form example.
But here it's used to fetch other accounts despite it's not the next item.
fetchMore will be called when the scrollbar reach the end.
I don't think it's a good use of the fetchMore capabilities
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok. let me handle that in a new PR together with a proxy model maybe.