mirror of
https://github.com/qTox/qTox.git
synced 2024-03-22 14:00:36 +08:00
Merge pull request #6073
Anthony Bilinski (1): fix(notification): hide snore warning log spam Mick Sayson (2): feat(notification): Notification string generator for multiple messages feat(notification): Notifications now always replace the previous one
This commit is contained in:
commit
431fd7bfa2
|
@ -369,6 +369,8 @@ set(${PROJECT_NAME}_SOURCES
|
||||||
src/model/chathistory.cpp
|
src/model/chathistory.cpp
|
||||||
src/model/toxclientstandards.h
|
src/model/toxclientstandards.h
|
||||||
src/model/ibootstraplistgenerator.h
|
src/model/ibootstraplistgenerator.h
|
||||||
|
src/model/notificationgenerator.h
|
||||||
|
src/model/notificationgenerator.cpp
|
||||||
src/net/bootstrapnodeupdater.cpp
|
src/net/bootstrapnodeupdater.cpp
|
||||||
src/net/bootstrapnodeupdater.h
|
src/net/bootstrapnodeupdater.h
|
||||||
src/net/avatarbroadcaster.cpp
|
src/net/avatarbroadcaster.cpp
|
||||||
|
|
|
@ -48,6 +48,7 @@ auto_test(model groupmessagedispatcher)
|
||||||
auto_test(model messageprocessor)
|
auto_test(model messageprocessor)
|
||||||
auto_test(model sessionchatlog)
|
auto_test(model sessionchatlog)
|
||||||
auto_test(model exiftransform)
|
auto_test(model exiftransform)
|
||||||
|
auto_test(model notificationgenerator)
|
||||||
|
|
||||||
if (UNIX)
|
if (UNIX)
|
||||||
auto_test(platform posixsignalnotifier)
|
auto_test(platform posixsignalnotifier)
|
||||||
|
|
10
src/main.cpp
10
src/main.cpp
|
@ -94,6 +94,16 @@ void logMessageHandler(QtMsgType type, const QMessageLogContext& ctxt, const QSt
|
||||||
&& msg == QString("QFSFileEngine::open: No file name specified"))
|
&& msg == QString("QFSFileEngine::open: No file name specified"))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
QRegExp snoreFilter{QStringLiteral("Snore::Notification.*was already closed")};
|
||||||
|
if (type == QtWarningMsg
|
||||||
|
&& msg.contains(snoreFilter))
|
||||||
|
{
|
||||||
|
// snorenotify logs this when we call requestCloseNotification correctly. The behaviour still works, so we'll
|
||||||
|
// just mask the warning for now. The issue has been reported upstream:
|
||||||
|
// https://github.com/qTox/qTox/pull/6073#pullrequestreview-420748519
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
QString file = ctxt.file;
|
QString file = ctxt.file;
|
||||||
// We're not using QT_MESSAGELOG_FILE here, because that can be 0, NULL, or
|
// We're not using QT_MESSAGELOG_FILE here, because that can be 0, NULL, or
|
||||||
// nullptr in release builds.
|
// nullptr in release builds.
|
||||||
|
|
30
src/model/notificationdata.h
Normal file
30
src/model/notificationdata.h
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
/*
|
||||||
|
Copyright © 2020 by The qTox Project Contributors
|
||||||
|
|
||||||
|
This file is part of qTox, a Qt-based graphical interface for Tox.
|
||||||
|
|
||||||
|
qTox is libre 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, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
qTox 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.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with qTox. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
#include <QPixmap>
|
||||||
|
|
||||||
|
struct NotificationData
|
||||||
|
{
|
||||||
|
QString title;
|
||||||
|
QString message;
|
||||||
|
QPixmap pixmap;
|
||||||
|
};
|
267
src/model/notificationgenerator.cpp
Normal file
267
src/model/notificationgenerator.cpp
Normal file
|
@ -0,0 +1,267 @@
|
||||||
|
/*
|
||||||
|
Copyright © 2020 by The qTox Project Contributors
|
||||||
|
|
||||||
|
This file is part of qTox, a Qt-based graphical interface for Tox.
|
||||||
|
|
||||||
|
qTox is libre 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, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
qTox 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.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with qTox. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "notificationgenerator.h"
|
||||||
|
#include "src/chatlog/content/filetransferwidget.h"
|
||||||
|
|
||||||
|
#include <QCollator>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
size_t getNumMessages(
|
||||||
|
const QHash<const Friend*, size_t>& friendNotifications,
|
||||||
|
const QHash<const Group* , size_t>& groupNotifications)
|
||||||
|
{
|
||||||
|
auto numMessages = std::accumulate(friendNotifications.begin(), friendNotifications.end(), 0);
|
||||||
|
numMessages = std::accumulate(groupNotifications.begin(), groupNotifications.end(), numMessages);
|
||||||
|
|
||||||
|
return numMessages;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t getNumChats(
|
||||||
|
const QHash<const Friend*, size_t>& friendNotifications,
|
||||||
|
const QHash<const Group* , size_t>& groupNotifications)
|
||||||
|
{
|
||||||
|
return friendNotifications.size() + groupNotifications.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString generateMultiChatTitle(size_t numChats, size_t numMessages)
|
||||||
|
{
|
||||||
|
//: e.g. 3 messages from 2 chats
|
||||||
|
return QObject::tr("%1 message(s) from %2 chats")
|
||||||
|
.arg(numMessages)
|
||||||
|
.arg(numChats);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
QString generateSingleChatTitle(
|
||||||
|
const QHash<T, size_t> numNotifications,
|
||||||
|
T contact)
|
||||||
|
{
|
||||||
|
if (numNotifications[contact] > 1)
|
||||||
|
{
|
||||||
|
//: e.g. 2 messages from Bob
|
||||||
|
return QObject::tr("%1 message(s) from %2")
|
||||||
|
.arg(numNotifications[contact])
|
||||||
|
.arg(contact->getDisplayedName());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return contact->getDisplayedName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString generateTitle(
|
||||||
|
const QHash<const Friend*, size_t>& friendNotifications,
|
||||||
|
const QHash<const Group* , size_t>& groupNotifications,
|
||||||
|
const Friend* f)
|
||||||
|
{
|
||||||
|
auto numChats = getNumChats(friendNotifications, groupNotifications);
|
||||||
|
if (numChats > 1)
|
||||||
|
{
|
||||||
|
return generateMultiChatTitle(numChats, getNumMessages(friendNotifications, groupNotifications));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return generateSingleChatTitle(friendNotifications, f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString generateTitle(
|
||||||
|
const QHash<const Friend*, size_t>& friendNotifications,
|
||||||
|
const QHash<const Group* , size_t>& groupNotifications,
|
||||||
|
const Group* g)
|
||||||
|
{
|
||||||
|
auto numChats = getNumChats(friendNotifications, groupNotifications);
|
||||||
|
if (numChats > 1)
|
||||||
|
{
|
||||||
|
return generateMultiChatTitle(numChats, getNumMessages(friendNotifications, groupNotifications));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return generateSingleChatTitle(groupNotifications, g);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString generateContent(
|
||||||
|
const QHash<const Friend*, size_t>& friendNotifications,
|
||||||
|
const QHash<const Group*, size_t>& groupNotifications,
|
||||||
|
QString lastMessage,
|
||||||
|
const ToxPk& sender)
|
||||||
|
{
|
||||||
|
assert(friendNotifications.size() > 0 || groupNotifications.size() > 0);
|
||||||
|
|
||||||
|
auto numChats = getNumChats(friendNotifications, groupNotifications);
|
||||||
|
if (numChats > 1) {
|
||||||
|
// Copy all names into a vector to simplify formatting logic between
|
||||||
|
// multiple lists
|
||||||
|
std::vector<QString> displayNames;
|
||||||
|
displayNames.reserve(numChats);
|
||||||
|
|
||||||
|
for (auto it = friendNotifications.begin(); it != friendNotifications.end(); ++it) {
|
||||||
|
displayNames.push_back(it.key()->getDisplayedName());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto it = groupNotifications.begin(); it != groupNotifications.end(); ++it) {
|
||||||
|
displayNames.push_back(it.key()->getDisplayedName());
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(displayNames.size() > 0);
|
||||||
|
|
||||||
|
// Lexiographically sort all display names to ensure consistent formatting
|
||||||
|
QCollator collator;
|
||||||
|
std::sort(displayNames.begin(), displayNames.end(), [&] (const QString& a, const QString& b) {
|
||||||
|
return collator.compare(a, b) < 1;
|
||||||
|
});
|
||||||
|
|
||||||
|
auto it = displayNames.begin();
|
||||||
|
|
||||||
|
QString ret = *it;
|
||||||
|
|
||||||
|
while (++it != displayNames.end()) {
|
||||||
|
ret += ", " + *it;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (groupNotifications.size() == 1) {
|
||||||
|
return groupNotifications.begin().key()->getPeerList()[sender] + ": " + lastMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
return lastMessage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QPixmap getSenderAvatar(Profile* profile, const ToxPk& sender)
|
||||||
|
{
|
||||||
|
return profile ? profile->loadAvatar(sender) : QPixmap();
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
NotificationGenerator::NotificationGenerator(
|
||||||
|
INotificationSettings const& notificationSettings,
|
||||||
|
Profile* profile)
|
||||||
|
: notificationSettings(notificationSettings)
|
||||||
|
, profile(profile)
|
||||||
|
{}
|
||||||
|
|
||||||
|
NotificationData NotificationGenerator::friendMessageNotification(const Friend* f, const QString& message)
|
||||||
|
{
|
||||||
|
friendNotifications[f]++;
|
||||||
|
|
||||||
|
NotificationData ret;
|
||||||
|
|
||||||
|
if (notificationSettings.getNotifyHide()) {
|
||||||
|
ret.title = tr("New message");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret.title = generateTitle(friendNotifications, groupNotifications, f);
|
||||||
|
ret.message = generateContent(friendNotifications, groupNotifications, message, f->getPublicKey());
|
||||||
|
ret.pixmap = getSenderAvatar(profile, f->getPublicKey());
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
NotificationData NotificationGenerator::groupMessageNotification(const Group* g, const ToxPk& sender, const QString& message)
|
||||||
|
{
|
||||||
|
groupNotifications[g]++;
|
||||||
|
|
||||||
|
NotificationData ret;
|
||||||
|
|
||||||
|
if (notificationSettings.getNotifyHide()){
|
||||||
|
ret.title = tr("New group message");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret.title = generateTitle(friendNotifications, groupNotifications, g);
|
||||||
|
ret.message = generateContent(friendNotifications, groupNotifications, message, sender);
|
||||||
|
ret.pixmap = getSenderAvatar(profile, sender);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
NotificationData NotificationGenerator::fileTransferNotification(const Friend* f, const QString& filename, size_t fileSize)
|
||||||
|
{
|
||||||
|
friendNotifications[f]++;
|
||||||
|
|
||||||
|
NotificationData ret;
|
||||||
|
|
||||||
|
if (notificationSettings.getNotifyHide()) {
|
||||||
|
ret.title = tr("Incoming file transfer");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto numChats = getNumChats(friendNotifications, groupNotifications);
|
||||||
|
auto numMessages = getNumMessages(friendNotifications, groupNotifications);
|
||||||
|
|
||||||
|
if (numChats > 1 || numMessages > 1)
|
||||||
|
{
|
||||||
|
ret.title = generateTitle(friendNotifications, groupNotifications, f);
|
||||||
|
ret.message = generateContent(friendNotifications, groupNotifications, tr("Incoming file transfer"), f->getPublicKey());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//: e.g. Bob - file transfer
|
||||||
|
ret.title = tr("%1 - file transfer").arg(f->getDisplayedName());
|
||||||
|
ret.message = filename + " (" + FileTransferWidget::getHumanReadableSize(fileSize) + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
ret.pixmap = getSenderAvatar(profile, f->getPublicKey());
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
NotificationData NotificationGenerator::groupInvitationNotification(const Friend* from)
|
||||||
|
{
|
||||||
|
NotificationData ret;
|
||||||
|
|
||||||
|
if (notificationSettings.getNotifyHide()) {
|
||||||
|
ret.title = tr("Group invite received");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret.title = tr("%1 invites you to join a group.").arg(from->getDisplayedName());
|
||||||
|
ret.message = "";
|
||||||
|
ret.pixmap = getSenderAvatar(profile, from->getPublicKey());
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
NotificationData NotificationGenerator::friendRequestNotification(const ToxPk& sender, const QString& message)
|
||||||
|
{
|
||||||
|
NotificationData ret;
|
||||||
|
|
||||||
|
if (notificationSettings.getNotifyHide()) {
|
||||||
|
ret.title = tr("Friend request received");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret.title = tr("Friend request received from %1").arg(sender.toString());
|
||||||
|
ret.message = message;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NotificationGenerator::onNotificationActivated()
|
||||||
|
{
|
||||||
|
friendNotifications = {};
|
||||||
|
groupNotifications = {};
|
||||||
|
}
|
57
src/model/notificationgenerator.h
Normal file
57
src/model/notificationgenerator.h
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
/*
|
||||||
|
Copyright © 2020 by The qTox Project Contributors
|
||||||
|
|
||||||
|
This file is part of qTox, a Qt-based graphical interface for Tox.
|
||||||
|
|
||||||
|
qTox is libre 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, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
qTox 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.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with qTox. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
|
||||||
|
#include "notificationdata.h"
|
||||||
|
#include "friend.h"
|
||||||
|
#include "group.h"
|
||||||
|
|
||||||
|
#include "src/persistence/inotificationsettings.h"
|
||||||
|
#include "src/persistence/profile.h"
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QHash>
|
||||||
|
|
||||||
|
class NotificationGenerator : public QObject
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NotificationGenerator(
|
||||||
|
INotificationSettings const& notificationSettings,
|
||||||
|
// Optional profile input to lookup avatars. Avatar lookup is not
|
||||||
|
// currently mockable so we allow profile to be nullptr for unit
|
||||||
|
// testing
|
||||||
|
Profile* profile);
|
||||||
|
|
||||||
|
NotificationData friendMessageNotification(const Friend* f, const QString& message);
|
||||||
|
NotificationData groupMessageNotification(const Group* g, const ToxPk& sender, const QString& message);
|
||||||
|
NotificationData fileTransferNotification(const Friend* f, const QString& filename, size_t fileSize);
|
||||||
|
NotificationData groupInvitationNotification(const Friend* from);
|
||||||
|
NotificationData friendRequestNotification(const ToxPk& sender, const QString& message);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void onNotificationActivated();
|
||||||
|
|
||||||
|
private:
|
||||||
|
INotificationSettings const& notificationSettings;
|
||||||
|
Profile* profile;
|
||||||
|
QHash<const Friend*, size_t> friendNotifications;
|
||||||
|
QHash<const Group*, size_t> groupNotifications;
|
||||||
|
};
|
|
@ -27,6 +27,4 @@ public:
|
||||||
virtual ~IGroupSettings() = default;
|
virtual ~IGroupSettings() = default;
|
||||||
virtual QStringList getBlackList() const = 0;
|
virtual QStringList getBlackList() const = 0;
|
||||||
virtual void setBlackList(const QStringList& blist) = 0;
|
virtual void setBlackList(const QStringList& blist) = 0;
|
||||||
virtual bool getGroupAlwaysNotify() const = 0;
|
|
||||||
virtual void setGroupAlwaysNotify(bool newValue) = 0;
|
|
||||||
};
|
};
|
||||||
|
|
47
src/persistence/inotificationsettings.h
Normal file
47
src/persistence/inotificationsettings.h
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
Copyright © 2020 by The qTox Project Contributors
|
||||||
|
|
||||||
|
This file is part of qTox, a Qt-based graphical interface for Tox.
|
||||||
|
|
||||||
|
qTox is libre 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, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
qTox 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.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with qTox. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QStringList>
|
||||||
|
|
||||||
|
class INotificationSettings
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual bool getNotify() const = 0;
|
||||||
|
virtual void setNotify(bool newValue) = 0;
|
||||||
|
|
||||||
|
virtual bool getShowWindow() const = 0;
|
||||||
|
virtual void setShowWindow(bool newValue) = 0;
|
||||||
|
|
||||||
|
virtual bool getDesktopNotify() const = 0;
|
||||||
|
virtual void setDesktopNotify(bool enabled) = 0;
|
||||||
|
|
||||||
|
virtual bool getNotifySound() const = 0;
|
||||||
|
virtual void setNotifySound(bool newValue) = 0;
|
||||||
|
|
||||||
|
virtual bool getNotifyHide() const = 0;
|
||||||
|
virtual void setNotifyHide(bool newValue) = 0;
|
||||||
|
|
||||||
|
virtual bool getBusySound() const = 0;
|
||||||
|
virtual void setBusySound(bool newValue) = 0;
|
||||||
|
|
||||||
|
virtual bool getGroupAlwaysNotify() const = 0;
|
||||||
|
virtual void setGroupAlwaysNotify(bool newValue) = 0;
|
||||||
|
};
|
|
@ -27,6 +27,7 @@
|
||||||
#include "src/persistence/paths.h"
|
#include "src/persistence/paths.h"
|
||||||
#include "src/persistence/ifriendsettings.h"
|
#include "src/persistence/ifriendsettings.h"
|
||||||
#include "src/persistence/igroupsettings.h"
|
#include "src/persistence/igroupsettings.h"
|
||||||
|
#include "src/persistence/inotificationsettings.h"
|
||||||
#include "src/video/ivideosettings.h"
|
#include "src/video/ivideosettings.h"
|
||||||
|
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
|
@ -50,7 +51,8 @@ class Settings : public QObject,
|
||||||
public IFriendSettings,
|
public IFriendSettings,
|
||||||
public IGroupSettings,
|
public IGroupSettings,
|
||||||
public IAudioSettings,
|
public IAudioSettings,
|
||||||
public IVideoSettings
|
public IVideoSettings,
|
||||||
|
public INotificationSettings
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
|
@ -311,23 +313,23 @@ public:
|
||||||
bool getCheckUpdates() const;
|
bool getCheckUpdates() const;
|
||||||
void setCheckUpdates(bool newValue);
|
void setCheckUpdates(bool newValue);
|
||||||
|
|
||||||
bool getNotify() const;
|
bool getNotify() const override;
|
||||||
void setNotify(bool newValue);
|
void setNotify(bool newValue) override;
|
||||||
|
|
||||||
bool getShowWindow() const;
|
bool getShowWindow() const override;
|
||||||
void setShowWindow(bool newValue);
|
void setShowWindow(bool newValue) override;
|
||||||
|
|
||||||
bool getDesktopNotify() const;
|
bool getDesktopNotify() const override;
|
||||||
void setDesktopNotify(bool enabled);
|
void setDesktopNotify(bool enabled) override;
|
||||||
|
|
||||||
bool getNotifySound() const;
|
bool getNotifySound() const override;
|
||||||
void setNotifySound(bool newValue);
|
void setNotifySound(bool newValue) override;
|
||||||
|
|
||||||
bool getNotifyHide() const;
|
bool getNotifyHide() const override;
|
||||||
void setNotifyHide(bool newValue);
|
void setNotifyHide(bool newValue) override;
|
||||||
|
|
||||||
bool getBusySound() const;
|
bool getBusySound() const override;
|
||||||
void setBusySound(bool newValue);
|
void setBusySound(bool newValue) override;
|
||||||
|
|
||||||
bool getGroupAlwaysNotify() const override;
|
bool getGroupAlwaysNotify() const override;
|
||||||
void setGroupAlwaysNotify(bool newValue) override;
|
void setGroupAlwaysNotify(bool newValue) override;
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
#include <libsnore/snore.h>
|
#include <libsnore/snore.h>
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
#include <QThread>
|
||||||
|
|
||||||
DesktopNotify::DesktopNotify()
|
DesktopNotify::DesktopNotify()
|
||||||
: notifyCore{Snore::SnoreCore::instance()}
|
: notifyCore{Snore::SnoreCore::instance()}
|
||||||
|
@ -37,43 +38,52 @@ DesktopNotify::DesktopNotify()
|
||||||
snoreApp = Snore::Application("qTox", snoreIcon);
|
snoreApp = Snore::Application("qTox", snoreIcon);
|
||||||
|
|
||||||
notifyCore.registerApplication(snoreApp);
|
notifyCore.registerApplication(snoreApp);
|
||||||
|
|
||||||
|
connect(¬ifyCore, &Snore::SnoreCore::notificationClosed, this, &DesktopNotify::onNotificationClose);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DesktopNotify::createNotification(const QString& title, const QString& text, Snore::Icon& icon)
|
void DesktopNotify::notifyMessage(const NotificationData& notificationData)
|
||||||
{
|
{
|
||||||
const Settings& s = Settings::getInstance();
|
const Settings& s = Settings::getInstance();
|
||||||
if(!(s.getNotify() && s.getDesktopNotify())) {
|
if(!(s.getNotify() && s.getDesktopNotify())) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Snore::Notification notify{snoreApp, Snore::Alert(), title, text, icon};
|
auto icon = notificationData.pixmap.isNull() ? snoreIcon : Snore::Icon(notificationData.pixmap);
|
||||||
|
auto newNotification = Snore::Notification{snoreApp, Snore::Alert(), notificationData.title, notificationData.message, icon, 0};
|
||||||
|
latestId = newNotification.id();
|
||||||
|
|
||||||
notifyCore.broadcastNotification(notify);
|
if (lastNotification.isValid()) {
|
||||||
}
|
// Workaround for broken updating behavior in snore. Snore increments
|
||||||
|
// the message count when a notification is updated. Snore also caps the
|
||||||
void DesktopNotify::notifyMessage(const QString& title, const QString& message)
|
// number of outgoing messages at 3. This means that if we update
|
||||||
{
|
// notifications more than 3 times we do not get notifications until the
|
||||||
createNotification(title, message, snoreIcon);
|
// user activates the notification.
|
||||||
}
|
//
|
||||||
|
// We work around this by closing the existing notification and replacing
|
||||||
void DesktopNotify::notifyMessagePixmap(const QString& title, const QString& message, QPixmap avatar)
|
// it with a new one. We then only process the notification close if the
|
||||||
{
|
// latest notification id is the same as the one we are closing. This allows
|
||||||
Snore::Icon new_icon(avatar);
|
// us to continue counting how many unread messages a user has until they
|
||||||
createNotification(title, message, new_icon);
|
// close the notification themselves.
|
||||||
}
|
//
|
||||||
|
// I've filed a bug on the snorenotify mailing list but the project seems
|
||||||
void DesktopNotify::notifyMessageSimple(const MessageType type)
|
// pretty dead. I filed a ticket on March 11 2020, and as of April 5 2020
|
||||||
{
|
// the moderators have not even acknowledged the message. A previous message
|
||||||
QString message;
|
// got a response starting with "Snorenotify isn't that well maintained any more"
|
||||||
switch (type) {
|
// (see https://mail.kde.org/pipermail/snorenotify/2019-March/000004.html)
|
||||||
case MessageType::FRIEND: message = tr("New message"); break;
|
// so I don't have hope of this being fixed any time soon
|
||||||
case MessageType::FRIEND_FILE: message = tr("Incoming file transfer"); break;
|
notifyCore.requestCloseNotification(lastNotification, Snore::Notification::CloseReasons::Dismissed);
|
||||||
case MessageType::FRIEND_REQUEST: message = tr("Friend request received"); break;
|
|
||||||
case MessageType::GROUP: message = tr("New group message"); break;
|
|
||||||
case MessageType::GROUP_INVITE: message = tr("Group invite received"); break;
|
|
||||||
default: break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
createNotification(message, {}, snoreIcon);
|
notifyCore.broadcastNotification(newNotification);
|
||||||
|
lastNotification = newNotification;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DesktopNotify::onNotificationClose(Snore::Notification notification)
|
||||||
|
{
|
||||||
|
if (notification.id() == latestId) {
|
||||||
|
lastNotification = {};
|
||||||
|
emit notificationClosed();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -19,11 +19,14 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#if DESKTOP_NOTIFICATIONS
|
#include "src/model/notificationdata.h"
|
||||||
|
|
||||||
#include <libsnore/snore.h>
|
#include <libsnore/snore.h>
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
class DesktopNotify : public QObject
|
class DesktopNotify : public QObject
|
||||||
{
|
{
|
||||||
|
@ -31,25 +34,19 @@ class DesktopNotify : public QObject
|
||||||
public:
|
public:
|
||||||
DesktopNotify();
|
DesktopNotify();
|
||||||
|
|
||||||
enum class MessageType {
|
|
||||||
FRIEND,
|
|
||||||
FRIEND_FILE,
|
|
||||||
FRIEND_REQUEST,
|
|
||||||
GROUP,
|
|
||||||
GROUP_INVITE
|
|
||||||
};
|
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void notifyMessage(const QString& title, const QString& message);
|
void notifyMessage(const NotificationData& notificationData);
|
||||||
void notifyMessagePixmap(const QString& title, const QString& message, QPixmap avatar);
|
|
||||||
void notifyMessageSimple(const MessageType type);
|
|
||||||
|
|
||||||
private:
|
signals:
|
||||||
void createNotification(const QString& title, const QString& text, Snore::Icon& icon);
|
void notificationClosed();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onNotificationClose(Snore::Notification notification);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Snore::SnoreCore& notifyCore;
|
Snore::SnoreCore& notifyCore;
|
||||||
Snore::Application snoreApp;
|
Snore::Application snoreApp;
|
||||||
Snore::Icon snoreIcon;
|
Snore::Icon snoreIcon;
|
||||||
|
Snore::Notification lastNotification;
|
||||||
|
uint latestId;
|
||||||
};
|
};
|
||||||
#endif // DESKTOP_NOTIFICATIONS
|
|
||||||
|
|
|
@ -294,6 +294,11 @@ void Widget::init()
|
||||||
profileInfo = new ProfileInfo(core, profile);
|
profileInfo = new ProfileInfo(core, profile);
|
||||||
profileForm = new ProfileForm(profileInfo);
|
profileForm = new ProfileForm(profileInfo);
|
||||||
|
|
||||||
|
#if DESKTOP_NOTIFICATIONS
|
||||||
|
notificationGenerator.reset(new NotificationGenerator(settings, profile));
|
||||||
|
connect(¬ifier, &DesktopNotify::notificationClosed, notificationGenerator.get(), &NotificationGenerator::onNotificationActivated);
|
||||||
|
#endif
|
||||||
|
|
||||||
// connect logout tray menu action
|
// connect logout tray menu action
|
||||||
connect(actionLogout, &QAction::triggered, profileForm, &ProfileForm::onLogoutClicked);
|
connect(actionLogout, &QAction::triggered, profileForm, &ProfileForm::onLogoutClicked);
|
||||||
|
|
||||||
|
@ -1498,7 +1503,7 @@ void Widget::addGroupDialog(Group* group, ContentDialog* dialog)
|
||||||
emit widget->chatroomWidgetClicked(widget);
|
emit widget->chatroomWidgetClicked(widget);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Widget::newFriendMessageAlert(const ToxPk& friendId, const QString& text, bool sound, bool file)
|
bool Widget::newFriendMessageAlert(const ToxPk& friendId, const QString& text, bool sound, QString filename, size_t filesize)
|
||||||
{
|
{
|
||||||
bool hasActive;
|
bool hasActive;
|
||||||
QWidget* currentWindow;
|
QWidget* currentWindow;
|
||||||
|
@ -1535,17 +1540,9 @@ bool Widget::newFriendMessageAlert(const ToxPk& friendId, const QString& text, b
|
||||||
widget->updateStatusLight();
|
widget->updateStatusLight();
|
||||||
ui->friendList->trackWidget(widget);
|
ui->friendList->trackWidget(widget);
|
||||||
#if DESKTOP_NOTIFICATIONS
|
#if DESKTOP_NOTIFICATIONS
|
||||||
if (settings.getNotifyHide()) {
|
auto notificationData = filename.isEmpty() ? notificationGenerator->friendMessageNotification(f, text)
|
||||||
notifier.notifyMessageSimple(file ? DesktopNotify::MessageType::FRIEND_FILE
|
: notificationGenerator->fileTransferNotification(f, filename, filesize);
|
||||||
: DesktopNotify::MessageType::FRIEND);
|
notifier.notifyMessage(notificationData);
|
||||||
} else {
|
|
||||||
QString title = f->getDisplayedName();
|
|
||||||
if (file) {
|
|
||||||
title += " - " + tr("File sent");
|
|
||||||
}
|
|
||||||
notifier.notifyMessagePixmap(title, text,
|
|
||||||
Nexus::getProfile()->loadAvatar(f->getPublicKey()));
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (contentDialog == nullptr) {
|
if (contentDialog == nullptr) {
|
||||||
|
@ -1586,18 +1583,8 @@ bool Widget::newGroupMessageAlert(const GroupId& groupId, const ToxPk& authorPk,
|
||||||
g->setEventFlag(true);
|
g->setEventFlag(true);
|
||||||
widget->updateStatusLight();
|
widget->updateStatusLight();
|
||||||
#if DESKTOP_NOTIFICATIONS
|
#if DESKTOP_NOTIFICATIONS
|
||||||
if (settings.getNotifyHide()) {
|
auto notificationData = notificationGenerator->groupMessageNotification(g, authorPk, message);
|
||||||
notifier.notifyMessageSimple(DesktopNotify::MessageType::GROUP);
|
notifier.notifyMessage(notificationData);
|
||||||
} else {
|
|
||||||
Friend* f = FriendList::findFriend(authorPk);
|
|
||||||
QString title = g->getPeerList().value(authorPk) + " (" + g->getDisplayedName() + ")";
|
|
||||||
if (!f) {
|
|
||||||
notifier.notifyMessage(title, message);
|
|
||||||
} else {
|
|
||||||
notifier.notifyMessagePixmap(title, message,
|
|
||||||
Nexus::getProfile()->loadAvatar(f->getPublicKey()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (contentDialog == nullptr) {
|
if (contentDialog == nullptr) {
|
||||||
|
@ -1673,11 +1660,8 @@ void Widget::onFriendRequestReceived(const ToxPk& friendPk, const QString& messa
|
||||||
friendRequestsUpdate();
|
friendRequestsUpdate();
|
||||||
newMessageAlert(window(), isActiveWindow(), true, true);
|
newMessageAlert(window(), isActiveWindow(), true, true);
|
||||||
#if DESKTOP_NOTIFICATIONS
|
#if DESKTOP_NOTIFICATIONS
|
||||||
if (settings.getNotifyHide()) {
|
auto notificationData = notificationGenerator->friendRequestNotification(friendPk, message);
|
||||||
notifier.notifyMessageSimple(DesktopNotify::MessageType::FRIEND_REQUEST);
|
notifier.notifyMessage(notificationData);
|
||||||
} else {
|
|
||||||
notifier.notifyMessage(friendPk.toString() + tr(" sent you a friend request."), message);
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1686,9 +1670,8 @@ void Widget::onFileReceiveRequested(const ToxFile& file)
|
||||||
{
|
{
|
||||||
const ToxPk& friendPk = FriendList::id2Key(file.friendId);
|
const ToxPk& friendPk = FriendList::id2Key(file.friendId);
|
||||||
newFriendMessageAlert(friendPk,
|
newFriendMessageAlert(friendPk,
|
||||||
file.fileName + " ("
|
{},
|
||||||
+ FileTransferWidget::getHumanReadableSize(file.filesize) + ")",
|
true, file.fileName, file.filesize);
|
||||||
true, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Widget::updateFriendActivity(const Friend& frnd)
|
void Widget::updateFriendActivity(const Friend& frnd)
|
||||||
|
@ -1934,12 +1917,8 @@ void Widget::onGroupInviteReceived(const GroupInvite& inviteInfo)
|
||||||
groupInvitesUpdate();
|
groupInvitesUpdate();
|
||||||
newMessageAlert(window(), isActiveWindow(), true, true);
|
newMessageAlert(window(), isActiveWindow(), true, true);
|
||||||
#if DESKTOP_NOTIFICATIONS
|
#if DESKTOP_NOTIFICATIONS
|
||||||
if (settings.getNotifyHide()) {
|
auto notificationData = notificationGenerator->groupInvitationNotification(f);
|
||||||
notifier.notifyMessageSimple(DesktopNotify::MessageType::GROUP_INVITE);
|
notifier.notifyMessage(notificationData);
|
||||||
} else {
|
|
||||||
notifier.notifyMessagePixmap(f->getDisplayedName() + tr(" invites you to join a group."),
|
|
||||||
{}, Nexus::getProfile()->loadAvatar(f->getPublicKey()));
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -38,6 +38,7 @@
|
||||||
#include "src/model/friendmessagedispatcher.h"
|
#include "src/model/friendmessagedispatcher.h"
|
||||||
#include "src/model/groupmessagedispatcher.h"
|
#include "src/model/groupmessagedispatcher.h"
|
||||||
#if DESKTOP_NOTIFICATIONS
|
#if DESKTOP_NOTIFICATIONS
|
||||||
|
#include "src/model/notificationgenerator.h"
|
||||||
#include "src/platform/desktop_notifications/desktopnotify.h"
|
#include "src/platform/desktop_notifications/desktopnotify.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -127,7 +128,7 @@ public:
|
||||||
void addFriendDialog(const Friend* frnd, ContentDialog* dialog);
|
void addFriendDialog(const Friend* frnd, ContentDialog* dialog);
|
||||||
void addGroupDialog(Group* group, ContentDialog* dialog);
|
void addGroupDialog(Group* group, ContentDialog* dialog);
|
||||||
bool newFriendMessageAlert(const ToxPk& friendId, const QString& text, bool sound = true,
|
bool newFriendMessageAlert(const ToxPk& friendId, const QString& text, bool sound = true,
|
||||||
bool file = false);
|
QString filename = QString(), size_t filesize = 0);
|
||||||
bool newGroupMessageAlert(const GroupId& groupId, const ToxPk& authorPk, const QString& message,
|
bool newGroupMessageAlert(const GroupId& groupId, const ToxPk& authorPk, const QString& message,
|
||||||
bool notify);
|
bool notify);
|
||||||
bool getIsWindowMinimized();
|
bool getIsWindowMinimized();
|
||||||
|
@ -360,6 +361,7 @@ private:
|
||||||
|
|
||||||
MessageProcessor::SharedParams sharedMessageProcessorParams;
|
MessageProcessor::SharedParams sharedMessageProcessorParams;
|
||||||
#if DESKTOP_NOTIFICATIONS
|
#if DESKTOP_NOTIFICATIONS
|
||||||
|
std::unique_ptr<NotificationGenerator> notificationGenerator;
|
||||||
DesktopNotify notifier;
|
DesktopNotify notifier;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
45
test/mock/mockcoreidhandler.h
Normal file
45
test/mock/mockcoreidhandler.h
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
/*
|
||||||
|
Copyright © 2020 by The qTox Project Contributors
|
||||||
|
|
||||||
|
This file is part of qTox, a Qt-based graphical interface for Tox.
|
||||||
|
|
||||||
|
qTox is libre 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, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
qTox 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.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with qTox. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "src/core/icoreidhandler.h"
|
||||||
|
|
||||||
|
#include <tox/tox.h>
|
||||||
|
|
||||||
|
class MockCoreIdHandler : public ICoreIdHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ToxId getSelfId() const override
|
||||||
|
{
|
||||||
|
std::terminate();
|
||||||
|
return ToxId();
|
||||||
|
}
|
||||||
|
|
||||||
|
ToxPk getSelfPublicKey() const override
|
||||||
|
{
|
||||||
|
static uint8_t id[TOX_PUBLIC_KEY_SIZE] = {0};
|
||||||
|
return ToxPk(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString getUsername() const override
|
||||||
|
{
|
||||||
|
return "me";
|
||||||
|
}
|
||||||
|
};
|
82
test/mock/mockgroupquery.h
Normal file
82
test/mock/mockgroupquery.h
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
/*
|
||||||
|
Copyright © 2020 by The qTox Project Contributors
|
||||||
|
|
||||||
|
This file is part of qTox, a Qt-based graphical interface for Tox.
|
||||||
|
|
||||||
|
qTox is libre 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, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
qTox 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.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with qTox. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "src/core/icoregroupquery.h"
|
||||||
|
|
||||||
|
#include <tox/tox.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mock 1 peer at group number 0
|
||||||
|
*/
|
||||||
|
class MockGroupQuery : public ICoreGroupQuery
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
GroupId getGroupPersistentId(uint32_t groupNumber) const override
|
||||||
|
{
|
||||||
|
return GroupId(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t getGroupNumberPeers(int groupId) const override
|
||||||
|
{
|
||||||
|
if (emptyGroup) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString getGroupPeerName(int groupId, int peerId) const override
|
||||||
|
{
|
||||||
|
return QString("peer") + peerId;
|
||||||
|
}
|
||||||
|
|
||||||
|
ToxPk getGroupPeerPk(int groupId, int peerId) const override
|
||||||
|
{
|
||||||
|
uint8_t id[TOX_PUBLIC_KEY_SIZE] = {static_cast<uint8_t>(peerId)};
|
||||||
|
return ToxPk(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList getGroupPeerNames(int groupId) const override
|
||||||
|
{
|
||||||
|
if (emptyGroup) {
|
||||||
|
return QStringList({QString("me")});
|
||||||
|
}
|
||||||
|
return QStringList({QString("me"), QString("other")});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool getGroupAvEnabled(int groupId) const override
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setAsEmptyGroup()
|
||||||
|
{
|
||||||
|
emptyGroup = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setAsFunctionalGroup()
|
||||||
|
{
|
||||||
|
emptyGroup = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool emptyGroup = false;
|
||||||
|
};
|
|
@ -23,6 +23,9 @@
|
||||||
#include "src/model/message.h"
|
#include "src/model/message.h"
|
||||||
#include "src/persistence/settings.h"
|
#include "src/persistence/settings.h"
|
||||||
|
|
||||||
|
#include "test/mock/mockcoreidhandler.h"
|
||||||
|
#include "test/mock/mockgroupquery.h"
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QtTest/QtTest>
|
#include <QtTest/QtTest>
|
||||||
|
|
||||||
|
@ -47,85 +50,6 @@ public:
|
||||||
size_t numSentMessages = 0;
|
size_t numSentMessages = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Mock 1 peer at group number 0
|
|
||||||
*/
|
|
||||||
class MockGroupQuery : public ICoreGroupQuery
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
GroupId getGroupPersistentId(uint32_t groupNumber) const override
|
|
||||||
{
|
|
||||||
return GroupId();
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t getGroupNumberPeers(int groupId) const override
|
|
||||||
{
|
|
||||||
if (emptyGroup) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString getGroupPeerName(int groupId, int peerId) const override
|
|
||||||
{
|
|
||||||
return QString("peer") + peerId;
|
|
||||||
}
|
|
||||||
|
|
||||||
ToxPk getGroupPeerPk(int groupId, int peerId) const override
|
|
||||||
{
|
|
||||||
uint8_t id[TOX_PUBLIC_KEY_SIZE] = {static_cast<uint8_t>(peerId)};
|
|
||||||
return ToxPk(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
QStringList getGroupPeerNames(int groupId) const override
|
|
||||||
{
|
|
||||||
if (emptyGroup) {
|
|
||||||
return QStringList({QString("me")});
|
|
||||||
}
|
|
||||||
return QStringList({QString("me"), QString("other")});
|
|
||||||
}
|
|
||||||
|
|
||||||
bool getGroupAvEnabled(int groupId) const override
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setAsEmptyGroup()
|
|
||||||
{
|
|
||||||
emptyGroup = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setAsFunctionalGroup()
|
|
||||||
{
|
|
||||||
emptyGroup = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool emptyGroup = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
class MockCoreIdHandler : public ICoreIdHandler
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
ToxId getSelfId() const override
|
|
||||||
{
|
|
||||||
std::terminate();
|
|
||||||
return ToxId();
|
|
||||||
}
|
|
||||||
|
|
||||||
ToxPk getSelfPublicKey() const override
|
|
||||||
{
|
|
||||||
static uint8_t id[TOX_PUBLIC_KEY_SIZE] = {0};
|
|
||||||
return ToxPk(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString getUsername() const override
|
|
||||||
{
|
|
||||||
return "me";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class MockGroupSettings : public IGroupSettings
|
class MockGroupSettings : public IGroupSettings
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -139,13 +63,6 @@ public:
|
||||||
blacklist = blist;
|
blacklist = blist;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool getGroupAlwaysNotify() const override
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setGroupAlwaysNotify(bool newValue) override {}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QStringList blacklist;
|
QStringList blacklist;
|
||||||
};
|
};
|
||||||
|
|
371
test/model/notificationgenerator_test.cpp
Normal file
371
test/model/notificationgenerator_test.cpp
Normal file
|
@ -0,0 +1,371 @@
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright © 2019 by The qTox Project Contributors
|
||||||
|
|
||||||
|
This file is part of qTox, a Qt-based graphical interface for Tox.
|
||||||
|
|
||||||
|
qTox is libre 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, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
qTox 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.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with qTox. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "src/model/notificationgenerator.h"
|
||||||
|
|
||||||
|
#include "test/mock/mockcoreidhandler.h"
|
||||||
|
#include "test/mock/mockgroupquery.h"
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QtTest/QtTest>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
class MockNotificationSettings : public INotificationSettings
|
||||||
|
{
|
||||||
|
virtual bool getNotify() const override { return true; }
|
||||||
|
|
||||||
|
virtual void setNotify(bool newValue) override {}
|
||||||
|
|
||||||
|
virtual bool getShowWindow() const override { return true; }
|
||||||
|
virtual void setShowWindow(bool newValue) override {}
|
||||||
|
|
||||||
|
virtual bool getDesktopNotify() const override { return true; }
|
||||||
|
virtual void setDesktopNotify(bool enabled) override {}
|
||||||
|
|
||||||
|
virtual bool getNotifySound() const override { return true; }
|
||||||
|
virtual void setNotifySound(bool newValue) override {}
|
||||||
|
|
||||||
|
virtual bool getNotifyHide() const override { return notifyHide; }
|
||||||
|
virtual void setNotifyHide(bool newValue) override { notifyHide = newValue; };
|
||||||
|
|
||||||
|
virtual bool getBusySound() const override { return true; }
|
||||||
|
virtual void setBusySound(bool newValue) override {}
|
||||||
|
|
||||||
|
virtual bool getGroupAlwaysNotify() const override { return true; }
|
||||||
|
virtual void setGroupAlwaysNotify(bool newValue) override {}
|
||||||
|
private:
|
||||||
|
bool notifyHide = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
class TestNotificationGenerator : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void init();
|
||||||
|
void testSingleFriendMessage();
|
||||||
|
void testMultipleFriendMessages();
|
||||||
|
void testNotificationClear();
|
||||||
|
void testGroupMessage();
|
||||||
|
void testMultipleGroupMessages();
|
||||||
|
void testMultipleFriendSourceMessages();
|
||||||
|
void testMultipleGroupSourceMessages();
|
||||||
|
void testMixedSourceMessages();
|
||||||
|
void testFileTransfer();
|
||||||
|
void testFileTransferAfterMessage();
|
||||||
|
void testGroupInvitation();
|
||||||
|
void testGroupInviteUncounted();
|
||||||
|
void testFriendRequest();
|
||||||
|
void testFriendRequestUncounted();
|
||||||
|
void testSimpleFriendMessage();
|
||||||
|
void testSimpleFileTransfer();
|
||||||
|
void testSimpleGroupMessage();
|
||||||
|
void testSimpleFriendRequest();
|
||||||
|
void testSimpleGroupInvite();
|
||||||
|
void testSimpleMessageToggle();
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unique_ptr<INotificationSettings> notificationSettings;
|
||||||
|
std::unique_ptr<NotificationGenerator> notificationGenerator;
|
||||||
|
std::unique_ptr<MockGroupQuery> groupQuery;
|
||||||
|
std::unique_ptr<MockCoreIdHandler> coreIdHandler;
|
||||||
|
};
|
||||||
|
|
||||||
|
void TestNotificationGenerator::init()
|
||||||
|
{
|
||||||
|
notificationSettings.reset(new MockNotificationSettings());
|
||||||
|
notificationGenerator.reset(new NotificationGenerator(*notificationSettings, nullptr));
|
||||||
|
groupQuery.reset(new MockGroupQuery());
|
||||||
|
coreIdHandler.reset(new MockCoreIdHandler());
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestNotificationGenerator::testSingleFriendMessage()
|
||||||
|
{
|
||||||
|
Friend f(0, ToxPk());
|
||||||
|
f.setName("friendName");
|
||||||
|
auto notificationData = notificationGenerator->friendMessageNotification(&f, "test");
|
||||||
|
QVERIFY(notificationData.title == "friendName");
|
||||||
|
QVERIFY(notificationData.message == "test");
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestNotificationGenerator::testMultipleFriendMessages()
|
||||||
|
{
|
||||||
|
Friend f(0, ToxPk());
|
||||||
|
f.setName("friendName");
|
||||||
|
notificationGenerator->friendMessageNotification(&f, "test");
|
||||||
|
auto notificationData = notificationGenerator->friendMessageNotification(&f, "test2");
|
||||||
|
QVERIFY(notificationData.title == "2 message(s) from friendName");
|
||||||
|
QVERIFY(notificationData.message == "test2");
|
||||||
|
|
||||||
|
notificationData = notificationGenerator->friendMessageNotification(&f, "test3");
|
||||||
|
QVERIFY(notificationData.title == "3 message(s) from friendName");
|
||||||
|
QVERIFY(notificationData.message == "test3");
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestNotificationGenerator::testNotificationClear()
|
||||||
|
{
|
||||||
|
Friend f(0, ToxPk());
|
||||||
|
f.setName("friendName");
|
||||||
|
|
||||||
|
notificationGenerator->friendMessageNotification(&f, "test");
|
||||||
|
|
||||||
|
// On notification clear we shouldn't see a notification count from the friend
|
||||||
|
notificationGenerator->onNotificationActivated();
|
||||||
|
|
||||||
|
auto notificationData = notificationGenerator->friendMessageNotification(&f, "test2");
|
||||||
|
QVERIFY(notificationData.title == "friendName");
|
||||||
|
QVERIFY(notificationData.message == "test2");
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestNotificationGenerator::testGroupMessage()
|
||||||
|
{
|
||||||
|
Group g(0, GroupId(0), "groupName", false, "selfName", *groupQuery, *coreIdHandler);
|
||||||
|
auto sender = groupQuery->getGroupPeerPk(0, 0);
|
||||||
|
g.updateUsername(sender, "sender1");
|
||||||
|
|
||||||
|
auto notificationData = notificationGenerator->groupMessageNotification(&g, sender, "test");
|
||||||
|
QVERIFY(notificationData.title == "groupName");
|
||||||
|
QVERIFY(notificationData.message == "sender1: test");
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestNotificationGenerator::testMultipleGroupMessages()
|
||||||
|
{
|
||||||
|
Group g(0, GroupId(0), "groupName", false, "selfName", *groupQuery, *coreIdHandler);
|
||||||
|
|
||||||
|
auto sender = groupQuery->getGroupPeerPk(0, 0);
|
||||||
|
g.updateUsername(sender, "sender1");
|
||||||
|
|
||||||
|
auto sender2 = groupQuery->getGroupPeerPk(0, 1);
|
||||||
|
g.updateUsername(sender2, "sender2");
|
||||||
|
|
||||||
|
notificationGenerator->groupMessageNotification(&g, sender, "test1");
|
||||||
|
|
||||||
|
auto notificationData = notificationGenerator->groupMessageNotification(&g, sender2, "test2");
|
||||||
|
QVERIFY(notificationData.title == "2 message(s) from groupName");
|
||||||
|
QVERIFY(notificationData.message == "sender2: test2");
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestNotificationGenerator::testMultipleFriendSourceMessages()
|
||||||
|
{
|
||||||
|
Friend f(0, ToxPk());
|
||||||
|
f.setName("friend1");
|
||||||
|
|
||||||
|
Friend f2(1, ToxPk());
|
||||||
|
f2.setName("friend2");
|
||||||
|
|
||||||
|
notificationGenerator->friendMessageNotification(&f, "test1");
|
||||||
|
auto notificationData = notificationGenerator->friendMessageNotification(&f2, "test2");
|
||||||
|
|
||||||
|
QVERIFY(notificationData.title == "2 message(s) from 2 chats");
|
||||||
|
QVERIFY(notificationData.message == "friend1, friend2");
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestNotificationGenerator::testMultipleGroupSourceMessages()
|
||||||
|
{
|
||||||
|
Group g(0, GroupId(QByteArray(32, 0)), "groupName", false, "selfName", *groupQuery, *coreIdHandler);
|
||||||
|
Group g2(1, GroupId(QByteArray(32, 1)), "groupName2", false, "selfName", *groupQuery, *coreIdHandler);
|
||||||
|
|
||||||
|
auto sender = groupQuery->getGroupPeerPk(0, 0);
|
||||||
|
g.updateUsername(sender, "sender1");
|
||||||
|
|
||||||
|
notificationGenerator->groupMessageNotification(&g, sender, "test1");
|
||||||
|
auto notificationData = notificationGenerator->groupMessageNotification(&g2, sender, "test1");
|
||||||
|
|
||||||
|
QVERIFY(notificationData.title == "2 message(s) from 2 chats");
|
||||||
|
QVERIFY(notificationData.message == "groupName, groupName2");
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestNotificationGenerator::testMixedSourceMessages()
|
||||||
|
{
|
||||||
|
Friend f(0, ToxPk());
|
||||||
|
f.setName("friend");
|
||||||
|
|
||||||
|
Group g(0, GroupId(QByteArray(32, 0)), "group", false, "selfName", *groupQuery, *coreIdHandler);
|
||||||
|
|
||||||
|
auto sender = groupQuery->getGroupPeerPk(0, 0);
|
||||||
|
g.updateUsername(sender, "sender1");
|
||||||
|
|
||||||
|
notificationGenerator->friendMessageNotification(&f, "test1");
|
||||||
|
auto notificationData = notificationGenerator->groupMessageNotification(&g, sender, "test2");
|
||||||
|
|
||||||
|
QVERIFY(notificationData.title == "2 message(s) from 2 chats");
|
||||||
|
QVERIFY(notificationData.message == "friend, group");
|
||||||
|
|
||||||
|
notificationData = notificationGenerator->fileTransferNotification(&f, "file", 0);
|
||||||
|
QVERIFY(notificationData.title == "3 message(s) from 2 chats");
|
||||||
|
QVERIFY(notificationData.message == "friend, group");
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestNotificationGenerator::testFileTransfer()
|
||||||
|
{
|
||||||
|
Friend f(0, ToxPk());
|
||||||
|
f.setName("friend");
|
||||||
|
|
||||||
|
auto notificationData = notificationGenerator->fileTransferNotification(&f, "file", 5 * 1024 * 1024 /* 5MB */);
|
||||||
|
|
||||||
|
QVERIFY(notificationData.title == "friend - file transfer");
|
||||||
|
QVERIFY(notificationData.message == "file (5.00MiB)");
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestNotificationGenerator::testFileTransferAfterMessage()
|
||||||
|
{
|
||||||
|
Friend f(0, ToxPk());
|
||||||
|
f.setName("friend");
|
||||||
|
|
||||||
|
notificationGenerator->friendMessageNotification(&f, "test1");
|
||||||
|
auto notificationData = notificationGenerator->fileTransferNotification(&f, "file", 5 * 1024 * 1024 /* 5MB */);
|
||||||
|
|
||||||
|
QVERIFY(notificationData.title == "2 message(s) from friend");
|
||||||
|
QVERIFY(notificationData.message == "Incoming file transfer");
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestNotificationGenerator::testGroupInvitation()
|
||||||
|
{
|
||||||
|
Friend f(0, ToxPk());
|
||||||
|
f.setName("friend");
|
||||||
|
|
||||||
|
auto notificationData = notificationGenerator->groupInvitationNotification(&f);
|
||||||
|
|
||||||
|
QVERIFY(notificationData.title == "friend invites you to join a group.");
|
||||||
|
QVERIFY(notificationData.message == "");
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestNotificationGenerator::testGroupInviteUncounted()
|
||||||
|
{
|
||||||
|
Friend f(0, ToxPk());
|
||||||
|
f.setName("friend");
|
||||||
|
|
||||||
|
notificationGenerator->friendMessageNotification(&f, "test");
|
||||||
|
notificationGenerator->groupInvitationNotification(&f);
|
||||||
|
auto notificationData = notificationGenerator->friendMessageNotification(&f, "test2");
|
||||||
|
|
||||||
|
QVERIFY(notificationData.title == "2 message(s) from friend");
|
||||||
|
QVERIFY(notificationData.message == "test2");
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestNotificationGenerator::testFriendRequest()
|
||||||
|
{
|
||||||
|
ToxPk sender(QByteArray(32, 0));
|
||||||
|
|
||||||
|
auto notificationData = notificationGenerator->friendRequestNotification(sender, "request");
|
||||||
|
|
||||||
|
QVERIFY(notificationData.title == "Friend request received from 0000000000000000000000000000000000000000000000000000000000000000");
|
||||||
|
QVERIFY(notificationData.message == "request");
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestNotificationGenerator::testFriendRequestUncounted()
|
||||||
|
{
|
||||||
|
Friend f(0, ToxPk());
|
||||||
|
f.setName("friend");
|
||||||
|
ToxPk sender(QByteArray(32, 0));
|
||||||
|
|
||||||
|
notificationGenerator->friendMessageNotification(&f, "test");
|
||||||
|
notificationGenerator->friendRequestNotification(sender, "request");
|
||||||
|
auto notificationData = notificationGenerator->friendMessageNotification(&f, "test2");
|
||||||
|
|
||||||
|
QVERIFY(notificationData.title == "2 message(s) from friend");
|
||||||
|
QVERIFY(notificationData.message == "test2");
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestNotificationGenerator::testSimpleFriendMessage()
|
||||||
|
{
|
||||||
|
Friend f(0, ToxPk());
|
||||||
|
f.setName("friend");
|
||||||
|
|
||||||
|
notificationSettings->setNotifyHide(true);
|
||||||
|
|
||||||
|
auto notificationData = notificationGenerator->friendMessageNotification(&f, "test");
|
||||||
|
|
||||||
|
QVERIFY(notificationData.title == "New message");
|
||||||
|
QVERIFY(notificationData.message == "");
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestNotificationGenerator::testSimpleFileTransfer()
|
||||||
|
{
|
||||||
|
Friend f(0, ToxPk());
|
||||||
|
f.setName("friend");
|
||||||
|
|
||||||
|
notificationSettings->setNotifyHide(true);
|
||||||
|
|
||||||
|
auto notificationData = notificationGenerator->fileTransferNotification(&f, "file", 0);
|
||||||
|
|
||||||
|
QVERIFY(notificationData.title == "Incoming file transfer");
|
||||||
|
QVERIFY(notificationData.message == "");
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestNotificationGenerator::testSimpleGroupMessage()
|
||||||
|
{
|
||||||
|
Group g(0, GroupId(0), "groupName", false, "selfName", *groupQuery, *coreIdHandler);
|
||||||
|
auto sender = groupQuery->getGroupPeerPk(0, 0);
|
||||||
|
g.updateUsername(sender, "sender1");
|
||||||
|
|
||||||
|
notificationSettings->setNotifyHide(true);
|
||||||
|
|
||||||
|
auto notificationData = notificationGenerator->groupMessageNotification(&g, sender, "test");
|
||||||
|
QVERIFY(notificationData.title == "New group message");
|
||||||
|
QVERIFY(notificationData.message == "");
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestNotificationGenerator::testSimpleFriendRequest()
|
||||||
|
{
|
||||||
|
ToxPk sender(QByteArray(32, 0));
|
||||||
|
|
||||||
|
notificationSettings->setNotifyHide(true);
|
||||||
|
|
||||||
|
auto notificationData = notificationGenerator->friendRequestNotification(sender, "request");
|
||||||
|
|
||||||
|
QVERIFY(notificationData.title == "Friend request received");
|
||||||
|
QVERIFY(notificationData.message == "");
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestNotificationGenerator::testSimpleGroupInvite()
|
||||||
|
{
|
||||||
|
Friend f(0, ToxPk());
|
||||||
|
f.setName("friend");
|
||||||
|
|
||||||
|
notificationSettings->setNotifyHide(true);
|
||||||
|
auto notificationData = notificationGenerator->groupInvitationNotification(&f);
|
||||||
|
|
||||||
|
QVERIFY(notificationData.title == "Group invite received");
|
||||||
|
QVERIFY(notificationData.message == "");
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestNotificationGenerator::testSimpleMessageToggle()
|
||||||
|
{
|
||||||
|
Friend f(0, ToxPk());
|
||||||
|
f.setName("friend");
|
||||||
|
|
||||||
|
notificationSettings->setNotifyHide(true);
|
||||||
|
|
||||||
|
notificationGenerator->friendMessageNotification(&f, "test");
|
||||||
|
|
||||||
|
notificationSettings->setNotifyHide(false);
|
||||||
|
|
||||||
|
auto notificationData = notificationGenerator->friendMessageNotification(&f, "test2");
|
||||||
|
|
||||||
|
QVERIFY(notificationData.title == "2 message(s) from friend");
|
||||||
|
QVERIFY(notificationData.message == "test2");
|
||||||
|
}
|
||||||
|
|
||||||
|
QTEST_GUILESS_MAIN(TestNotificationGenerator)
|
||||||
|
#include "notificationgenerator_test.moc"
|
Loading…
Reference in New Issue
Block a user