2021-02-05 16:56:02 +01:00
|
|
|
/*
|
|
|
|
|
* This file is part of the Flowee project
|
2022-04-29 22:43:46 +02:00
|
|
|
* Copyright (C) 2021-2022 Tom Zander <tom@flowee.org>
|
2021-02-05 16:56:02 +01:00
|
|
|
*
|
|
|
|
|
* 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, either version 3 of the License, or
|
|
|
|
|
* (at your option) any later version.
|
|
|
|
|
*
|
|
|
|
|
* 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.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
*/
|
|
|
|
|
#include "NotificationManager.h"
|
2022-04-30 14:52:40 +02:00
|
|
|
#include "FloweePay.h"
|
2022-04-30 19:54:55 +02:00
|
|
|
#include "PriceDataProvider.h"
|
2021-02-05 16:56:02 +01:00
|
|
|
#include <utils/Logger.h>
|
2022-04-30 20:22:46 +02:00
|
|
|
|
2022-04-29 22:43:46 +02:00
|
|
|
#include <QDBusConnection>
|
|
|
|
|
#include <QDBusInterface>
|
2022-04-30 20:21:43 +02:00
|
|
|
#include <QSettings>
|
2021-02-05 16:56:02 +01:00
|
|
|
|
2022-04-30 20:21:43 +02:00
|
|
|
constexpr const char *MUTE = "notificationNewblockMute";
|
|
|
|
|
|
2021-02-05 16:56:02 +01:00
|
|
|
NotificationManager::NotificationManager(QObject *parent)
|
2022-05-01 17:36:00 +02:00
|
|
|
: QObject(parent),
|
|
|
|
|
m_startupTime(QDateTime::currentDateTimeUtc())
|
2021-02-05 16:56:02 +01:00
|
|
|
{
|
|
|
|
|
setCollation(true);
|
2022-04-29 22:43:46 +02:00
|
|
|
|
|
|
|
|
// We use the notification spec (v 1.2)
|
|
|
|
|
// https://specifications.freedesktop.org/notification-spec/notification-spec-latest.html
|
2022-04-30 14:52:40 +02:00
|
|
|
m_newBlockHints["desktop-entry"] = "org.flowee.pay";
|
2022-04-29 22:43:46 +02:00
|
|
|
m_newBlockHints["urgency"] = 0; // low
|
|
|
|
|
// "sound-file" // filename TODO
|
2022-04-30 14:52:40 +02:00
|
|
|
m_walletUpdateHints["desktop-entry"] = "org.flowee.pay";
|
2022-04-29 22:43:46 +02:00
|
|
|
m_walletUpdateHints["urgency"] = 1; // normal
|
|
|
|
|
|
|
|
|
|
#if QT_DBUS_LIB
|
|
|
|
|
// setup slots for DBUS-signals. Essentially remote callbacks from the deskop notifications app
|
|
|
|
|
QDBusConnection::sessionBus().connect(QString(), QString(), "org.freedesktop.Notifications",
|
|
|
|
|
"NotificationClosed", this, SLOT(notificationClosed(uint,uint)));
|
|
|
|
|
QDBusConnection::sessionBus().connect(QString(), QString(), "org.freedesktop.Notifications",
|
|
|
|
|
"ActionInvoked", this, SLOT(actionInvoked(uint,QString)));
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
connect (this, SIGNAL(newBlockSeenSignal(int)), this, SLOT(newBlockSeen(int)), Qt::QueuedConnection);
|
2022-04-30 14:52:40 +02:00
|
|
|
connect (this, SIGNAL(segmentUpdatedSignal()), this, SLOT(walletUpdated()), Qt::QueuedConnection);
|
2022-04-29 22:43:46 +02:00
|
|
|
|
2022-04-30 20:21:43 +02:00
|
|
|
QSettings appConfig;
|
|
|
|
|
m_newBlockMuted = appConfig.value(MUTE, false).toBool();
|
2021-02-05 16:56:02 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void NotificationManager::notifyNewBlock(const P2PNet::Notification ¬ification)
|
|
|
|
|
{
|
2022-04-29 22:43:46 +02:00
|
|
|
emit newBlockSeenSignal(notification.blockHeight); // move to the Qt thread
|
2021-02-05 16:56:02 +01:00
|
|
|
}
|
|
|
|
|
|
2022-04-30 14:52:40 +02:00
|
|
|
void NotificationManager::segmentUpdated(const P2PNet::Notification&)
|
2021-02-05 16:56:02 +01:00
|
|
|
{
|
2022-04-30 14:52:40 +02:00
|
|
|
emit segmentUpdatedSignal(); // move to the Qt thread
|
2021-02-05 16:56:02 +01:00
|
|
|
}
|
2022-04-29 22:43:46 +02:00
|
|
|
|
2022-08-15 21:03:31 +02:00
|
|
|
bool NotificationManager::newBlockMuted() const
|
|
|
|
|
{
|
|
|
|
|
return m_newBlockMuted;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void NotificationManager::setNewBlockMuted(bool mute)
|
|
|
|
|
{
|
|
|
|
|
if (m_newBlockMuted == mute)
|
|
|
|
|
return;
|
|
|
|
|
m_newBlockMuted = mute;
|
|
|
|
|
QSettings appConfig;
|
|
|
|
|
appConfig.setValue(MUTE, m_newBlockMuted);
|
|
|
|
|
emit newBlockMutedChanged();
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-29 22:43:46 +02:00
|
|
|
void NotificationManager::newBlockSeen(int blockHeight)
|
|
|
|
|
{
|
|
|
|
|
if (m_newBlockMuted)
|
|
|
|
|
return;
|
2022-05-01 17:36:00 +02:00
|
|
|
if (m_startupTime.secsTo(QDateTime::currentDateTimeUtc()) < 60) {
|
|
|
|
|
// When Flowee Pay first starts it synchronizes with the network
|
|
|
|
|
// and we get a 'new block seen' notification almost every time.
|
|
|
|
|
// This is annoying and useless, so lets ignore the new blocks
|
|
|
|
|
// happening the first minute or so. Which is almost always
|
|
|
|
|
// enough time to sync the headers.
|
|
|
|
|
return;
|
|
|
|
|
}
|
2022-04-29 22:43:46 +02:00
|
|
|
logCritical() << "new block" << blockHeight;
|
|
|
|
|
#if QT_DBUS_LIB
|
|
|
|
|
auto iface = remote();
|
|
|
|
|
if (!iface->isValid()) return;
|
|
|
|
|
QVariantList args;
|
|
|
|
|
args << QVariant("Flowee Pay"); // app-name
|
|
|
|
|
args << QVariant(m_blockNotificationId); // replaces-id
|
2022-04-30 14:52:40 +02:00
|
|
|
args << QString(); // app_icon (not needed since we say which desktop file we are)
|
2022-04-29 22:43:46 +02:00
|
|
|
args << QString(); // body-text
|
2022-04-30 14:52:40 +02:00
|
|
|
args << tr("BCH block mined %1").arg(blockHeight); // summary text
|
|
|
|
|
// args << QString(); // body-text
|
2022-04-29 22:43:46 +02:00
|
|
|
QStringList actions; // actions
|
|
|
|
|
actions << "mute" << tr("Mute");
|
|
|
|
|
args << actions;
|
|
|
|
|
args << m_newBlockHints;
|
|
|
|
|
args << -1; // timeout (ms) -1 means to let the server decide
|
|
|
|
|
if (!iface->callWithCallback("Notify", args, this,
|
|
|
|
|
SLOT(newBlockNotificationShown(uint)))) {
|
|
|
|
|
logWarning() << "dbus down, can't show notifications";
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void NotificationManager::newBlockNotificationShown(uint id)
|
|
|
|
|
{
|
|
|
|
|
m_blockNotificationId = id;
|
|
|
|
|
logDebug() << "new block notification id:" << m_blockNotificationId;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void NotificationManager::notificationClosed(uint32_t id, uint32_t reason)
|
|
|
|
|
{
|
|
|
|
|
logDebug() << " something got closed" << id << reason;
|
|
|
|
|
if (m_blockNotificationId == id) {
|
|
|
|
|
m_blockNotificationId = 0;
|
|
|
|
|
}
|
|
|
|
|
else if (m_newFundsNotificationId == id) {
|
|
|
|
|
m_newFundsNotificationId = 0;
|
2022-04-30 14:52:40 +02:00
|
|
|
flushCollate();
|
2022-04-29 22:43:46 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void NotificationManager::actionInvoked(uint, const QString &actionKey)
|
|
|
|
|
{
|
2022-08-15 21:03:31 +02:00
|
|
|
if (actionKey == "mute")
|
|
|
|
|
setNewBlockMuted(true);
|
2022-04-29 22:43:46 +02:00
|
|
|
}
|
|
|
|
|
|
2022-04-30 14:52:40 +02:00
|
|
|
void NotificationManager::walletUpdated()
|
2022-04-29 22:43:46 +02:00
|
|
|
{
|
2022-04-30 14:52:40 +02:00
|
|
|
const auto data = collatedData();
|
|
|
|
|
if (data.empty())
|
|
|
|
|
return;
|
|
|
|
|
|
2022-05-01 19:41:41 +02:00
|
|
|
if (m_openingNewFundsNotification) {
|
|
|
|
|
// we are currently (async) opening a notification, wait until its actually open.
|
|
|
|
|
QTimer::singleShot(50, this, SLOT(walletUpdated()));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-30 14:52:40 +02:00
|
|
|
int64_t deposited = 0;
|
|
|
|
|
int64_t spent = 0;
|
|
|
|
|
int txCount = 0;
|
|
|
|
|
for (const auto &item : data) {
|
|
|
|
|
deposited += item.deposited;
|
|
|
|
|
spent += item.spent;
|
|
|
|
|
txCount += item.txCount;
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-29 22:43:46 +02:00
|
|
|
#if QT_DBUS_LIB
|
|
|
|
|
auto iface = remote();
|
|
|
|
|
if (!iface->isValid()) return;
|
|
|
|
|
QVariantList args;
|
|
|
|
|
args << QVariant("Flowee Pay"); // app-name
|
|
|
|
|
args << QVariant(m_newFundsNotificationId); // replaces-id
|
2022-04-30 14:52:40 +02:00
|
|
|
args << QString(); // app_icon (not needed since we say which desktop file we are)
|
|
|
|
|
args << tr("New Transaction", "", txCount);
|
|
|
|
|
|
2022-04-30 19:54:55 +02:00
|
|
|
const auto gained = deposited - spent;
|
|
|
|
|
auto pricesOracle = FloweePay::instance()->prices();
|
|
|
|
|
QString gainedStr;
|
2022-08-17 16:42:49 +02:00
|
|
|
if (pricesOracle->price())
|
|
|
|
|
gainedStr = pricesOracle->formattedPrice(gained, pricesOracle->price());
|
|
|
|
|
if (gainedStr.isEmpty()) {
|
2022-04-30 19:54:55 +02:00
|
|
|
// no price data available (yet). Display crypto units
|
|
|
|
|
gainedStr = QString("%1 %2")
|
2022-08-14 16:46:23 +02:00
|
|
|
.arg(FloweePay::instance()->amountToStringPretty((double) gained),
|
2022-04-30 19:54:55 +02:00
|
|
|
FloweePay::instance()->unitName());
|
|
|
|
|
}
|
2022-08-17 16:42:49 +02:00
|
|
|
if (gained > 0) // since we indicate adding, we always want the plus there
|
|
|
|
|
gainedStr = QString("+%1").arg(gainedStr);
|
2022-04-30 19:54:55 +02:00
|
|
|
|
2022-04-30 14:52:40 +02:00
|
|
|
// body-text
|
|
|
|
|
if (data.size() > 1) {
|
|
|
|
|
args << tr("%1 new transactions across %2 wallets found (%3)")
|
|
|
|
|
.arg(txCount).arg(data.size())
|
2022-04-30 19:54:55 +02:00
|
|
|
.arg(gainedStr);
|
2022-08-17 16:42:49 +02:00
|
|
|
}
|
|
|
|
|
else if (gained < 0 && txCount == 1) {
|
|
|
|
|
args << tr("A payment of %1 has been sent")
|
|
|
|
|
.arg(gainedStr.mid(1));
|
|
|
|
|
}
|
|
|
|
|
else {
|
2022-04-30 14:52:40 +02:00
|
|
|
args << tr("%1 new transactions found (%2)", "", txCount)
|
|
|
|
|
.arg(txCount)
|
2022-04-30 19:54:55 +02:00
|
|
|
.arg(gainedStr);
|
2022-04-30 14:52:40 +02:00
|
|
|
}
|
|
|
|
|
|
2022-04-29 22:43:46 +02:00
|
|
|
args << QStringList();
|
|
|
|
|
args << m_walletUpdateHints;
|
2022-04-30 14:52:40 +02:00
|
|
|
args << -1; // timeout (ms) -1 means to let the server decide
|
2022-04-29 22:43:46 +02:00
|
|
|
if (!iface->callWithCallback("Notify", args, this,
|
|
|
|
|
SLOT(walletUpdateNotificationShown(uint)))) {
|
|
|
|
|
logWarning() << "dbus down, can't show notifications";
|
|
|
|
|
}
|
2022-05-01 19:41:41 +02:00
|
|
|
if (m_newFundsNotificationId == 0) {
|
|
|
|
|
// The assignment of m_newFundsNotificationId is async, as such
|
|
|
|
|
// we want to remember we are opening one and not have multiple calls in
|
|
|
|
|
// flight at the same time
|
|
|
|
|
m_openingNewFundsNotification = true;
|
|
|
|
|
}
|
2022-04-29 22:43:46 +02:00
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void NotificationManager::walletUpdateNotificationShown(uint id)
|
|
|
|
|
{
|
|
|
|
|
m_newFundsNotificationId = id;
|
2022-05-01 19:41:41 +02:00
|
|
|
m_openingNewFundsNotification = false;
|
2022-04-29 22:43:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if QT_DBUS_LIB
|
|
|
|
|
QDBusInterface *NotificationManager::remote()
|
|
|
|
|
{
|
|
|
|
|
if (m_remote == nullptr) {
|
|
|
|
|
m_remote = new QDBusInterface("org.freedesktop.Notifications", // service
|
|
|
|
|
"/org/freedesktop/Notifications", // path
|
|
|
|
|
"org.freedesktop.Notifications", // interface name (duplicate of service)
|
|
|
|
|
QDBusConnection::sessionBus(), this);
|
|
|
|
|
if (!m_remote->isValid())
|
|
|
|
|
logWarning() << qPrintable(QDBusConnection::sessionBus().lastError().message());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return m_remote;
|
|
|
|
|
}
|
|
|
|
|
#endif
|