mirror of
https://github.com/nextcloud/desktop.git
synced 2024-11-22 21:15:55 +03:00
Merge pull request #197 from nextcloud/fix-notifications
Improves notifications: - Display the actual response text from the notification API in the popups. - Do not display sync activities messages like how many files were downloaded, only errors. - Make the notifications section above the activity feed also clickable. e.g open the call, the file that was shared, open the calendar event etc - Changes "Show desktop notifications" setting to "Show server notifications" - Show notifications on startup and then show only NEW notifications: uses If-None-Match check to only retrieve notifications once there are new notifications.
This commit is contained in:
commit
08b33c6f4b
20 changed files with 278 additions and 125 deletions
Binary file not shown.
Before Width: | Height: | Size: 900 B After Width: | Height: | Size: 495 B |
|
@ -431,27 +431,38 @@ QString Utility::timeAgoInWords(const QDateTime &dt, const QDateTime &from)
|
|||
now = from;
|
||||
}
|
||||
|
||||
if (dt.daysTo(now) > 0) {
|
||||
int dtn = dt.daysTo(now);
|
||||
return QObject::tr("%n day(s) ago", "", dtn);
|
||||
if (dt.daysTo(now) == 1) {
|
||||
return QObject::tr("%n day ago", "", dt.daysTo(now));
|
||||
} else if (dt.daysTo(now) > 1) {
|
||||
return QObject::tr("%n days ago", "", dt.daysTo(now));
|
||||
} else {
|
||||
qint64 secs = dt.secsTo(now);
|
||||
if (secs < 0) {
|
||||
return QObject::tr("in the future");
|
||||
}
|
||||
|
||||
if (floor(secs / 3600.0) > 0) {
|
||||
int hours = floor(secs / 3600.0);
|
||||
return (QObject::tr("%n hour(s) ago", "", hours));
|
||||
if(hours == 1){
|
||||
return (QObject::tr("%n hour ago", "", hours));
|
||||
} else {
|
||||
return (QObject::tr("%n hours ago", "", hours));
|
||||
}
|
||||
} else {
|
||||
int minutes = qRound(secs / 60.0);
|
||||
|
||||
if (minutes == 0) {
|
||||
if (secs < 5) {
|
||||
return QObject::tr("now");
|
||||
} else {
|
||||
return QObject::tr("Less than a minute ago");
|
||||
}
|
||||
|
||||
} else if(minutes == 1){
|
||||
return (QObject::tr("%n minute ago", "", minutes));
|
||||
} else {
|
||||
return (QObject::tr("%n minutes ago", "", minutes));
|
||||
}
|
||||
return (QObject::tr("%n minute(s) ago", "", minutes));
|
||||
}
|
||||
}
|
||||
return QObject::tr("Some time ago");
|
||||
|
|
|
@ -35,6 +35,7 @@ AccountState::AccountState(AccountPtr account)
|
|||
, _connectionStatus(ConnectionValidator::Undefined)
|
||||
, _waitingForNewCredentials(false)
|
||||
, _maintenanceToConnectedDelay(60000 + (qrand() % (4 * 60000))) // 1-5min delay
|
||||
, _notificationsEtagResponseHeader("*")
|
||||
{
|
||||
qRegisterMetaType<AccountState *>("AccountState*");
|
||||
|
||||
|
@ -176,6 +177,16 @@ void AccountState::tagLastSuccessfullETagRequest()
|
|||
_timeSinceLastETagCheck.start();
|
||||
}
|
||||
|
||||
QByteArray AccountState::notificationsEtagResponseHeader() const
|
||||
{
|
||||
return _notificationsEtagResponseHeader;
|
||||
}
|
||||
|
||||
void AccountState::setNotificationsEtagResponseHeader(const QByteArray &value)
|
||||
{
|
||||
_notificationsEtagResponseHeader = value;
|
||||
}
|
||||
|
||||
void AccountState::checkConnectivity()
|
||||
{
|
||||
if (isSignedOut() || _waitingForNewCredentials) {
|
||||
|
|
|
@ -131,6 +131,16 @@ public:
|
|||
*/
|
||||
void tagLastSuccessfullETagRequest();
|
||||
|
||||
/** Saves the ETag Response header from the last Notifications api
|
||||
* request with statusCode 200.
|
||||
*/
|
||||
QByteArray notificationsEtagResponseHeader() const;
|
||||
|
||||
/** Returns the ETag Response header from the last Notifications api
|
||||
* request with statusCode 200.
|
||||
*/
|
||||
void setNotificationsEtagResponseHeader(const QByteArray &value);
|
||||
|
||||
public slots:
|
||||
/// Triggers a ping to the server to update state and
|
||||
/// connection status and errors.
|
||||
|
@ -157,6 +167,7 @@ private:
|
|||
bool _waitingForNewCredentials;
|
||||
QElapsedTimer _timeSinceLastETagCheck;
|
||||
QPointer<ConnectionValidator> _connectionValidator;
|
||||
QByteArray _notificationsEtagResponseHeader;
|
||||
|
||||
/**
|
||||
* Starts counting when the server starts being back up after 503 or
|
||||
|
|
|
@ -53,14 +53,16 @@ QVariant ActivityListModel::data(const QModelIndex &index, int role) const
|
|||
|
||||
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));
|
||||
if(!a._file.isEmpty()){
|
||||
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;
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
#include "servernotificationhandler.h"
|
||||
#include "theme.h"
|
||||
#include "ocsjob.h"
|
||||
#include "configfile.h"
|
||||
|
||||
#include "ui_activitywidget.h"
|
||||
|
||||
|
@ -303,6 +304,16 @@ void ActivityWidget::slotBuildNotificationDisplay(const ActivityList &list)
|
|||
}
|
||||
}
|
||||
_guiLoggedNotifications.insert(activity._id);
|
||||
|
||||
// Assemble a tray notification for the NEW notification
|
||||
ConfigFile cfg;
|
||||
if(cfg.optionalServerNotifications()){
|
||||
if(AccountManager::instance()->accounts().count() == 1){
|
||||
emit guiLog(activity._subject, "");
|
||||
} else {
|
||||
emit guiLog(activity._subject, activity._accName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -339,31 +350,9 @@ void ActivityWidget::slotBuildNotificationDisplay(const ActivityList &list)
|
|||
|
||||
checkActivityTabVisibility();
|
||||
|
||||
int newGuiLogCount = accNotified.count();
|
||||
|
||||
if (newGuiLogCount > 0) {
|
||||
if (newNotificationShown) {
|
||||
// restart the gui log timer now that we show a notification
|
||||
_guiLogTimer.start();
|
||||
|
||||
// Assemble a tray notification
|
||||
QString msg = tr("You received %n new notification(s) from %2.", "", accNotified[accNotified.keys().at(0)]).arg(accNotified.keys().at(0));
|
||||
|
||||
if (newGuiLogCount >= 2) {
|
||||
QString acc1 = accNotified.keys().at(0);
|
||||
QString acc2 = accNotified.keys().at(1);
|
||||
if (newGuiLogCount == 2) {
|
||||
int notiCount = accNotified[acc1] + accNotified[acc2];
|
||||
msg = tr("You received %n new notification(s) from %1 and %2.", "", notiCount).arg(acc1, acc2);
|
||||
} else {
|
||||
msg = tr("You received new notifications from %1, %2 and other accounts.").arg(acc1, acc2);
|
||||
}
|
||||
}
|
||||
|
||||
const QString log = tr("%1 Notifications - Action Required").arg(Theme::instance()->appNameGUI());
|
||||
emit guiLog(log, msg);
|
||||
}
|
||||
|
||||
if (newNotificationShown) {
|
||||
emit newNotification();
|
||||
}
|
||||
}
|
||||
|
@ -615,6 +604,7 @@ void ActivitySettings::slotCopyToClipboard()
|
|||
}
|
||||
|
||||
QApplication::clipboard()->setText(text);
|
||||
|
||||
emit guiLog(tr("Copied to clipboard"), message);
|
||||
}
|
||||
|
||||
|
|
|
@ -409,7 +409,9 @@ void Folder::createGuiLog(const QString &filename, LogStatus status, int count,
|
|||
}
|
||||
|
||||
if (!text.isEmpty()) {
|
||||
logger->postOptionalGuiLog(tr("Sync Activity"), text);
|
||||
// Ignores the settings in case of an error or conflict
|
||||
if(status == LogStatusError || status == LogStatusConflict)
|
||||
logger->postOptionalGuiLog(tr("Sync Activity"), text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,8 +42,10 @@ GeneralSettings::GeneralSettings(QWidget *parent)
|
|||
{
|
||||
_ui->setupUi(this);
|
||||
|
||||
connect(_ui->desktopNotificationsCheckBox, &QAbstractButton::toggled,
|
||||
this, &GeneralSettings::slotToggleOptionalDesktopNotifications);
|
||||
connect(_ui->serverNotificationsCheckBox, &QAbstractButton::toggled,
|
||||
this, &GeneralSettings::slotToggleOptionalServerNotifications);
|
||||
_ui->serverNotificationsCheckBox->setToolTip(tr("Server notifications that require attention."));
|
||||
|
||||
connect(_ui->showInExplorerNavigationPaneCheckBox, &QAbstractButton::toggled, this, &GeneralSettings::slotShowInExplorerNavigationPane);
|
||||
|
||||
_ui->autostartCheckBox->setChecked(Utility::hasLaunchOnStartup(Theme::instance()->appName()));
|
||||
|
@ -114,7 +116,7 @@ void GeneralSettings::loadMiscSettings()
|
|||
QScopedValueRollback<bool> scope(_currentlyLoading, true);
|
||||
ConfigFile cfgFile;
|
||||
_ui->monoIconsCheckBox->setChecked(cfgFile.monoIcons());
|
||||
_ui->desktopNotificationsCheckBox->setChecked(cfgFile.optionalDesktopNotifications());
|
||||
_ui->serverNotificationsCheckBox->setChecked(cfgFile.optionalServerNotifications());
|
||||
_ui->showInExplorerNavigationPaneCheckBox->setChecked(cfgFile.showInExplorerNavigationPane());
|
||||
_ui->crashreporterCheckBox->setChecked(cfgFile.crashReporter());
|
||||
auto newFolderLimit = cfgFile.newBigFolderSizeLimit();
|
||||
|
@ -165,10 +167,10 @@ void GeneralSettings::slotToggleLaunchOnStartup(bool enable)
|
|||
Utility::setLaunchOnStartup(theme->appName(), theme->appNameGUI(), enable);
|
||||
}
|
||||
|
||||
void GeneralSettings::slotToggleOptionalDesktopNotifications(bool enable)
|
||||
void GeneralSettings::slotToggleOptionalServerNotifications(bool enable)
|
||||
{
|
||||
ConfigFile cfgFile;
|
||||
cfgFile.setOptionalDesktopNotifications(enable);
|
||||
cfgFile.setOptionalServerNotifications(enable);
|
||||
}
|
||||
|
||||
void GeneralSettings::slotShowInExplorerNavigationPane(bool checked)
|
||||
|
|
|
@ -42,7 +42,7 @@ public:
|
|||
private slots:
|
||||
void saveMiscSettings();
|
||||
void slotToggleLaunchOnStartup(bool);
|
||||
void slotToggleOptionalDesktopNotifications(bool);
|
||||
void slotToggleOptionalServerNotifications(bool);
|
||||
void slotShowInExplorerNavigationPane(bool);
|
||||
void slotUpdateInfo();
|
||||
void slotIgnoreFilesEditor();
|
||||
|
|
|
@ -20,6 +20,16 @@
|
|||
<string>General Settings</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="1" column="0">
|
||||
<widget class="QCheckBox" name="monoIconsCheckBox">
|
||||
<property name="toolTip">
|
||||
<string>For System Tray</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Use &Monochrome Icons</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QCheckBox" name="autostartCheckBox">
|
||||
<property name="text">
|
||||
|
@ -28,19 +38,9 @@
|
|||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QCheckBox" name="desktopNotificationsCheckBox">
|
||||
<widget class="QCheckBox" name="serverNotificationsCheckBox">
|
||||
<property name="text">
|
||||
<string>Show &Desktop Notifications</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QCheckBox" name="monoIconsCheckBox">
|
||||
<property name="toolTip">
|
||||
<string>For System Tray</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Use &Monochrome Icons</string>
|
||||
<string>Show Server &Notifications</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -253,7 +253,7 @@
|
|||
</widget>
|
||||
<tabstops>
|
||||
<tabstop>autostartCheckBox</tabstop>
|
||||
<tabstop>desktopNotificationsCheckBox</tabstop>
|
||||
<tabstop>serverNotificationsCheckBox</tabstop>
|
||||
<tabstop>monoIconsCheckBox</tabstop>
|
||||
<tabstop>ignoredFilesButton</tabstop>
|
||||
<tabstop>newFolderLimitCheckBox</tabstop>
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include "QProgressIndicator.h"
|
||||
#include "common/utility.h"
|
||||
#include "common/asserts.h"
|
||||
#include "guiutility.h"
|
||||
|
||||
#include <QPushButton>
|
||||
|
||||
|
@ -40,7 +41,6 @@ void NotificationWidget::setActivity(const Activity &activity)
|
|||
_accountName = activity._accName;
|
||||
ASSERT(!_accountName.isEmpty());
|
||||
|
||||
// _ui._headerLabel->setText( );
|
||||
_ui._subjectLabel->setVisible(!activity._subject.isEmpty());
|
||||
_ui._messageLabel->setVisible(!activity._message.isEmpty());
|
||||
|
||||
|
@ -48,11 +48,11 @@ void NotificationWidget::setActivity(const Activity &activity)
|
|||
_ui._messageLabel->setText(activity._message);
|
||||
|
||||
_ui._notifIcon->setPixmap(QPixmap(":/client/resources/bell.png"));
|
||||
_ui._notifIcon->setMinimumWidth(64);
|
||||
_ui._notifIcon->setMinimumHeight(64);
|
||||
_ui._notifIcon->setMinimumWidth(22);
|
||||
_ui._notifIcon->setMinimumHeight(22);
|
||||
_ui._notifIcon->show();
|
||||
|
||||
QString tText = tr("Created at %1").arg(Utility::timeAgoInWords(activity._dateTime));
|
||||
QString tText = tr("%1").arg(Utility::timeAgoInWords(activity._dateTime));
|
||||
_ui._timeLabel->setText(tText);
|
||||
|
||||
// always remove the buttons
|
||||
|
@ -61,8 +61,18 @@ void NotificationWidget::setActivity(const Activity &activity)
|
|||
}
|
||||
_buttons.clear();
|
||||
|
||||
// open the notification in the browser if there is a link
|
||||
if(!_myActivity._link.isEmpty()){
|
||||
QString buttonText(tr("More information"));
|
||||
QPushButton *openBrowser = _ui._buttonBox->addButton(buttonText, QDialogButtonBox::AcceptRole);
|
||||
openBrowser->setDefault(true);
|
||||
connect(openBrowser, &QAbstractButton::clicked, this, &NotificationWidget::slotOpenBrowserButtonClicked);
|
||||
_buttons.prepend(openBrowser);
|
||||
}
|
||||
|
||||
// display buttons for the links
|
||||
if (activity._links.isEmpty()) {
|
||||
// is there any case where this code is executed?
|
||||
// in case there is no action defined, do a close button.
|
||||
QPushButton *b = _ui._buttonBox->addButton(QDialogButtonBox::Close);
|
||||
b->setDefault(true);
|
||||
|
@ -83,6 +93,11 @@ Activity NotificationWidget::activity() const
|
|||
return _myActivity;
|
||||
}
|
||||
|
||||
void NotificationWidget::slotOpenBrowserButtonClicked(){
|
||||
QUrl url(_myActivity._link);
|
||||
Utility::openBrowser(url, this);
|
||||
}
|
||||
|
||||
void NotificationWidget::slotButtonClicked()
|
||||
{
|
||||
QObject *buttonWidget = QObject::sender();
|
||||
|
@ -97,6 +112,10 @@ void NotificationWidget::slotButtonClicked()
|
|||
_buttons.at(i)->setEnabled(false);
|
||||
}
|
||||
|
||||
// there is an extra button: 'Open'
|
||||
if(!_myActivity._link.isEmpty())
|
||||
index--;
|
||||
|
||||
// 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
|
||||
|
|
|
@ -46,6 +46,7 @@ public slots:
|
|||
|
||||
private slots:
|
||||
void slotButtonClicked();
|
||||
void slotOpenBrowserButtonClicked();
|
||||
|
||||
private:
|
||||
Ui_NotificationWidget _ui;
|
||||
|
|
|
@ -20,42 +20,88 @@
|
|||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SetMaximumSize</enum>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>24</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>26</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="_notifIcon">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="pixmap">
|
||||
<pixmap>../../../../resources/bell.png</pixmap>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<property name="horizontalSpacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="verticalSpacing">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<item row="0" column="1">
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="spacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="_subjectLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
<layout class="QHBoxLayout" name="labelsHorizontalLayout" stretch="1,1">
|
||||
<property name="spacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Lorem ipsum dolor sit amet</string>
|
||||
<property name="topMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
</widget>
|
||||
<item>
|
||||
<widget class="QLabel" name="_notifIcon">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="lineWidth">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="pixmap">
|
||||
<pixmap>../../../../resources/bell.png</pixmap>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="_subjectLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Lorem ipsum dolor sit amet</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="_messageLabel">
|
||||
|
@ -71,13 +117,41 @@
|
|||
<property name="scaledContents">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="indent">
|
||||
<number>28</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<property name="spacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Preferred</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>28</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="_timeLabel">
|
||||
<property name="font">
|
||||
|
@ -89,7 +163,7 @@
|
|||
<string>TextLabel</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -104,19 +178,6 @@
|
|||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="2">
|
||||
<widget class="QFrame" name="frame">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::HLine</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<property name="lineWidth">
|
||||
<number>4</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
#include "ocsnavigationappsjob.h"
|
||||
#include "theme.h"
|
||||
#include "folderman.h"
|
||||
#include "configfile.h"
|
||||
#include "progressdispatcher.h"
|
||||
#include "owncloudsetupwizard.h"
|
||||
#include "sharedialog.h"
|
||||
|
@ -689,10 +688,7 @@ void ownCloudGui::slotShowTrayMessage(const QString &title, const QString &msg)
|
|||
|
||||
void ownCloudGui::slotShowOptionalTrayMessage(const QString &title, const QString &msg)
|
||||
{
|
||||
ConfigFile cfg;
|
||||
if (cfg.optionalDesktopNotifications()) {
|
||||
slotShowTrayMessage(title, msg);
|
||||
}
|
||||
slotShowTrayMessage(title, msg);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -24,7 +24,10 @@ namespace OCC {
|
|||
|
||||
Q_LOGGING_CATEGORY(lcServerNotification, "gui.servernotification", QtInfoMsg)
|
||||
|
||||
const QString notificationsPath = QLatin1String("ocs/v2.php/apps/notifications/api/v1/notifications");
|
||||
const QString notificationsPath = QLatin1String("ocs/v2.php/apps/notifications/api/v2/notifications");
|
||||
const char propertyAccountStateC[] = "oc_account_state";
|
||||
const int successStatusCode = 200;
|
||||
const int notModifiedStatusCode = 304;
|
||||
|
||||
ServerNotificationHandler::ServerNotificationHandler(QObject *parent)
|
||||
: QObject(parent)
|
||||
|
@ -52,22 +55,38 @@ void ServerNotificationHandler::slotFetchNotifications(AccountState *ptr)
|
|||
_notificationJob = new JsonApiJob(ptr->account(), notificationsPath, this);
|
||||
QObject::connect(_notificationJob.data(), &JsonApiJob::jsonReceived,
|
||||
this, &ServerNotificationHandler::slotNotificationsReceived);
|
||||
_notificationJob->setProperty("AccountStatePtr", QVariant::fromValue<AccountState *>(ptr));
|
||||
|
||||
QObject::connect(_notificationJob.data(), &JsonApiJob::etagResponseHeaderReceived,
|
||||
this, &ServerNotificationHandler::slotEtagResponseHeaderReceived);
|
||||
_notificationJob->setProperty(propertyAccountStateC, QVariant::fromValue<AccountState *>(ptr));
|
||||
_notificationJob->addRawHeader("If-None-Match", ptr->notificationsEtagResponseHeader());
|
||||
_notificationJob->start();
|
||||
}
|
||||
|
||||
void ServerNotificationHandler::slotEtagResponseHeaderReceived(const QByteArray &value, int statusCode){
|
||||
if(statusCode == successStatusCode){
|
||||
qCWarning(lcServerNotification) << "New Notification ETag Response Header received " << value;
|
||||
AccountState *account = qvariant_cast<AccountState *>(sender()->property(propertyAccountStateC));
|
||||
account->setNotificationsEtagResponseHeader(value);
|
||||
}
|
||||
}
|
||||
|
||||
void ServerNotificationHandler::slotNotificationsReceived(const QJsonDocument &json, int statusCode)
|
||||
{
|
||||
if (statusCode != 200) {
|
||||
if (statusCode != successStatusCode && statusCode != notModifiedStatusCode) {
|
||||
qCWarning(lcServerNotification) << "Notifications failed with status code " << statusCode;
|
||||
deleteLater();
|
||||
return;
|
||||
}
|
||||
|
||||
if (statusCode == notModifiedStatusCode) {
|
||||
qCWarning(lcServerNotification) << "Status code " << statusCode << " Not Modified - No new notifications.";
|
||||
deleteLater();
|
||||
return;
|
||||
}
|
||||
|
||||
auto notifies = json.object().value("ocs").toObject().value("data").toArray();
|
||||
|
||||
AccountState *ai = qvariant_cast<AccountState *>(sender()->property("AccountStatePtr"));
|
||||
AccountState *ai = qvariant_cast<AccountState *>(sender()->property(propertyAccountStateC));
|
||||
|
||||
ActivityList list;
|
||||
|
||||
|
@ -79,9 +98,15 @@ void ServerNotificationHandler::slotNotificationsReceived(const QJsonDocument &j
|
|||
a._id = json.value("notification_id").toInt();
|
||||
a._subject = json.value("subject").toString();
|
||||
a._message = json.value("message").toString();
|
||||
|
||||
QString s = json.value("link").toString();
|
||||
if (!s.isEmpty()) {
|
||||
a._link = QUrl(s);
|
||||
QUrl link(s);
|
||||
if(link.host().isEmpty()){
|
||||
link.setScheme(ai->account()->url().scheme());
|
||||
link.setHost(ai->account()->url().host());
|
||||
}
|
||||
a._link = link;
|
||||
}
|
||||
a._dateTime = QDateTime::fromString(json.value("datetime").toString(), Qt::ISODate);
|
||||
|
||||
|
|
|
@ -37,6 +37,7 @@ public slots:
|
|||
|
||||
private slots:
|
||||
void slotNotificationsReceived(const QJsonDocument &json, int statusCode);
|
||||
void slotEtagResponseHeaderReceived(const QByteArray &value, int statusCode);
|
||||
|
||||
private:
|
||||
QPointer<JsonApiJob> _notificationJob;
|
||||
|
|
|
@ -53,7 +53,7 @@ static const char notificationRefreshIntervalC[] = "notificationRefreshInterval"
|
|||
static const char monoIconsC[] = "monoIcons";
|
||||
static const char promptDeleteC[] = "promptDeleteAllFiles";
|
||||
static const char crashReporterC[] = "crashReporter";
|
||||
static const char optionalDesktopNoficationsC[] = "optionalDesktopNotifications";
|
||||
static const char optionalServerNotificationsC[] = "optionalServerNotifications";
|
||||
static const char showInExplorerNavigationPaneC[] = "showInExplorerNavigationPane";
|
||||
static const char skipUpdateCheckC[] = "skipUpdateCheck";
|
||||
static const char updateCheckIntervalC[] = "updateCheckInterval";
|
||||
|
@ -121,10 +121,10 @@ bool ConfigFile::setConfDir(const QString &value)
|
|||
return false;
|
||||
}
|
||||
|
||||
bool ConfigFile::optionalDesktopNotifications() const
|
||||
bool ConfigFile::optionalServerNotifications() const
|
||||
{
|
||||
QSettings settings(configFile(), QSettings::IniFormat);
|
||||
return settings.value(QLatin1String(optionalDesktopNoficationsC), true).toBool();
|
||||
return settings.value(QLatin1String(optionalServerNotificationsC), true).toBool();
|
||||
}
|
||||
|
||||
bool ConfigFile::showInExplorerNavigationPane() const
|
||||
|
@ -177,10 +177,10 @@ quint64 ConfigFile::targetChunkUploadDuration() const
|
|||
return settings.value(QLatin1String(targetChunkUploadDurationC), 60 * 1000).toLongLong(); // default to 1 minute
|
||||
}
|
||||
|
||||
void ConfigFile::setOptionalDesktopNotifications(bool show)
|
||||
void ConfigFile::setOptionalServerNotifications(bool show)
|
||||
{
|
||||
QSettings settings(configFile(), QSettings::IniFormat);
|
||||
settings.setValue(QLatin1String(optionalDesktopNoficationsC), show);
|
||||
settings.setValue(QLatin1String(optionalServerNotificationsC), show);
|
||||
settings.sync();
|
||||
}
|
||||
|
||||
|
|
|
@ -120,8 +120,8 @@ public:
|
|||
|
||||
static bool setConfDir(const QString &value);
|
||||
|
||||
bool optionalDesktopNotifications() const;
|
||||
void setOptionalDesktopNotifications(bool show);
|
||||
bool optionalServerNotifications() const;
|
||||
void setOptionalServerNotifications(bool show);
|
||||
|
||||
bool showInExplorerNavigationPane() const;
|
||||
void setShowInExplorerNavigationPane(bool show);
|
||||
|
|
|
@ -49,6 +49,7 @@ Q_LOGGING_CATEGORY(lcMkColJob, "sync.networkjob.mkcol", QtInfoMsg)
|
|||
Q_LOGGING_CATEGORY(lcProppatchJob, "sync.networkjob.proppatch", QtInfoMsg)
|
||||
Q_LOGGING_CATEGORY(lcJsonApiJob, "sync.networkjob.jsonapi", QtInfoMsg)
|
||||
Q_LOGGING_CATEGORY(lcDetermineAuthTypeJob, "sync.networkjob.determineauthtype", QtInfoMsg)
|
||||
const int notModifiedStatusCode = 304;
|
||||
|
||||
RequestEtagJob::RequestEtagJob(AccountPtr account, const QString &path, QObject *parent)
|
||||
: AbstractNetworkJob(account, path, parent)
|
||||
|
@ -789,14 +790,18 @@ void JsonApiJob::addQueryParams(const QUrlQuery ¶ms)
|
|||
_additionalParams = params;
|
||||
}
|
||||
|
||||
void JsonApiJob::addRawHeader(const QByteArray &headerName, const QByteArray &value)
|
||||
{
|
||||
_request.setRawHeader(headerName, value);
|
||||
}
|
||||
|
||||
void JsonApiJob::start()
|
||||
{
|
||||
QNetworkRequest req;
|
||||
req.setRawHeader("OCS-APIREQUEST", "true");
|
||||
addRawHeader("OCS-APIREQUEST", "true");
|
||||
auto query = _additionalParams;
|
||||
query.addQueryItem(QLatin1String("format"), QLatin1String("json"));
|
||||
QUrl url = Utility::concatUrlPath(account()->url(), path(), query);
|
||||
sendRequest("GET", url, req);
|
||||
sendRequest("GET", url, _request);
|
||||
AbstractNetworkJob::start();
|
||||
}
|
||||
|
||||
|
@ -807,9 +812,9 @@ bool JsonApiJob::finished()
|
|||
<< (reply()->error() == QNetworkReply::NoError ? QLatin1String("") : errorString());
|
||||
|
||||
int statusCode = 0;
|
||||
|
||||
int httpStatusCode = reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
||||
if (reply()->error() != QNetworkReply::NoError) {
|
||||
qCWarning(lcJsonApiJob) << "Network error: " << path() << errorString() << reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute);
|
||||
qCWarning(lcJsonApiJob) << "Network error: " << path() << errorString() << httpStatusCode;
|
||||
emit jsonReceived(QJsonDocument(), statusCode);
|
||||
return true;
|
||||
}
|
||||
|
@ -821,7 +826,9 @@ bool JsonApiJob::finished()
|
|||
// this is a error message coming back from ocs.
|
||||
statusCode = rex.cap(1).toInt();
|
||||
}
|
||||
|
||||
} else if(jsonStr.isEmpty() && httpStatusCode == notModifiedStatusCode){
|
||||
qCWarning(lcJsonApiJob) << "Nothing changed so nothing to retrieve - status code: " << httpStatusCode;
|
||||
statusCode = httpStatusCode;
|
||||
} else {
|
||||
QRegExp rex("\"statuscode\":(\\d+),");
|
||||
// example: "{"ocs":{"meta":{"status":"ok","statuscode":100,"message":null},"data":{"version":{"major":8,"minor":"... (504)
|
||||
|
@ -830,10 +837,14 @@ bool JsonApiJob::finished()
|
|||
}
|
||||
}
|
||||
|
||||
// save new ETag value
|
||||
if(reply()->rawHeaderList().contains("ETag"))
|
||||
emit etagResponseHeaderReceived(reply()->rawHeader("ETag"), statusCode);
|
||||
|
||||
QJsonParseError error;
|
||||
auto json = QJsonDocument::fromJson(jsonStr.toUtf8(), &error);
|
||||
// empty or invalid response
|
||||
if (error.error != QJsonParseError::NoError || json.isNull()) {
|
||||
// empty or invalid response and status code is != 304 because jsonStr is expected to be empty
|
||||
if ((error.error != QJsonParseError::NoError || json.isNull()) && httpStatusCode != notModifiedStatusCode) {
|
||||
qCWarning(lcJsonApiJob) << "invalid JSON!" << jsonStr << error.errorString();
|
||||
emit jsonReceived(json, statusCode);
|
||||
return true;
|
||||
|
|
|
@ -341,6 +341,7 @@ public:
|
|||
* This function needs to be called before start() obviously.
|
||||
*/
|
||||
void addQueryParams(const QUrlQuery ¶ms);
|
||||
void addRawHeader(const QByteArray &headerName, const QByteArray &value);
|
||||
|
||||
public slots:
|
||||
void start() Q_DECL_OVERRIDE;
|
||||
|
@ -356,8 +357,17 @@ signals:
|
|||
*/
|
||||
void jsonReceived(const QJsonDocument &json, int statusCode);
|
||||
|
||||
/**
|
||||
* @brief etagResponseHeaderReceived - signal to report the ETag response header value
|
||||
* from ocs api v2
|
||||
* @param value - the ETag response header value
|
||||
* @param statusCode - the OCS status code: 100 (!) for success
|
||||
*/
|
||||
void etagResponseHeaderReceived(const QByteArray &value, int statusCode);
|
||||
|
||||
private:
|
||||
QUrlQuery _additionalParams;
|
||||
QNetworkRequest _request;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in a new issue