2020-05-29 22:06:40 +04:00
|
|
|
|
2016-10-02 12:30:28 +03:00
|
|
|
/*
|
|
|
|
This file is part of Telegram Desktop,
|
2018-01-03 13:23:14 +03:00
|
|
|
the official desktop application for the Telegram messaging service.
|
2016-10-02 12:30:28 +03:00
|
|
|
|
2018-01-03 13:23:14 +03:00
|
|
|
For license and copyright information please follow this link:
|
|
|
|
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
2016-10-02 12:30:28 +03:00
|
|
|
*/
|
|
|
|
#include "platform/linux/notifications_manager_linux.h"
|
|
|
|
|
2022-11-11 15:49:50 +04:00
|
|
|
#include "base/options.h"
|
2020-06-17 01:23:39 +04:00
|
|
|
#include "base/platform/base_platform_info.h"
|
2021-02-28 06:34:41 +04:00
|
|
|
#include "base/platform/linux/base_linux_dbus_utilities.h"
|
2020-06-18 22:04:16 +04:00
|
|
|
#include "core/application.h"
|
2022-11-11 15:49:50 +04:00
|
|
|
#include "core/sandbox.h"
|
2020-06-18 22:04:16 +04:00
|
|
|
#include "core/core_settings.h"
|
2022-10-13 00:23:14 +04:00
|
|
|
#include "data/data_forum_topic.h"
|
2018-01-25 17:19:14 +03:00
|
|
|
#include "history/history.h"
|
2021-10-22 18:15:16 +04:00
|
|
|
#include "history/history_item.h"
|
2022-11-26 21:59:50 +04:00
|
|
|
#include "ui/empty_userpic.h"
|
2020-06-19 16:59:31 +04:00
|
|
|
#include "main/main_session.h"
|
2017-04-13 11:27:10 +03:00
|
|
|
#include "lang/lang_keys.h"
|
2021-05-03 14:22:58 +04:00
|
|
|
#include "base/weak_ptr.h"
|
2022-12-05 16:18:10 +04:00
|
|
|
#include "window/notifications_utilities.h"
|
2022-11-26 21:59:50 +04:00
|
|
|
#include "styles/style_window.h"
|
2016-10-03 11:56:03 +03:00
|
|
|
|
2022-11-26 21:59:50 +04:00
|
|
|
#include <QtCore/QBuffer>
|
2020-01-24 13:26:32 +01:00
|
|
|
#include <QtCore/QVersionNumber>
|
2021-03-12 08:55:31 +04:00
|
|
|
#include <QtGui/QGuiApplication>
|
2020-08-01 11:03:17 +00:00
|
|
|
|
2021-02-28 06:34:41 +04:00
|
|
|
#include <glibmm.h>
|
|
|
|
#include <giomm.h>
|
2019-12-29 15:41:45 +00:00
|
|
|
|
2022-11-11 15:49:50 +04:00
|
|
|
#include <dlfcn.h>
|
|
|
|
|
2016-10-02 12:30:28 +03:00
|
|
|
namespace Platform {
|
|
|
|
namespace Notifications {
|
2016-10-03 11:56:03 +03:00
|
|
|
namespace {
|
|
|
|
|
2023-07-20 18:06:45 +04:00
|
|
|
constexpr auto kService = "org.freedesktop.Notifications";
|
|
|
|
constexpr auto kObjectPath = "/org/freedesktop/Notifications";
|
2019-12-29 15:41:45 +00:00
|
|
|
constexpr auto kInterface = kService;
|
2023-07-20 18:06:45 +04:00
|
|
|
constexpr auto kPropertiesInterface = "org.freedesktop.DBus.Properties";
|
2019-12-29 15:41:45 +00:00
|
|
|
|
2023-10-02 15:47:15 +04:00
|
|
|
using PropertyMap = std::map<Glib::ustring, Glib::VariantBase>;
|
|
|
|
|
2021-01-16 08:47:07 +04:00
|
|
|
struct ServerInformation {
|
2023-09-19 13:41:50 +04:00
|
|
|
Glib::ustring name;
|
|
|
|
Glib::ustring vendor;
|
2021-01-16 08:47:07 +04:00
|
|
|
QVersionNumber version;
|
|
|
|
QVersionNumber specVersion;
|
|
|
|
};
|
|
|
|
|
2021-01-09 06:10:06 +04:00
|
|
|
bool ServiceRegistered = false;
|
2023-09-19 15:25:30 +04:00
|
|
|
ServerInformation CurrentServerInformation;
|
2023-09-19 13:41:50 +04:00
|
|
|
std::vector<Glib::ustring> CurrentCapabilities;
|
2020-03-10 12:36:06 +04:00
|
|
|
|
2023-10-02 17:54:15 +04:00
|
|
|
[[nodiscard]] bool HasCapability(const char *value) {
|
|
|
|
return ranges::contains(CurrentCapabilities, value, &Glib::ustring::raw);
|
|
|
|
}
|
|
|
|
|
2022-07-31 11:10:17 +04:00
|
|
|
void Noexcept(Fn<void()> callback, Fn<void()> failed = nullptr) noexcept {
|
2022-07-26 05:45:22 +04:00
|
|
|
try {
|
|
|
|
callback();
|
2022-07-31 11:10:17 +04:00
|
|
|
return;
|
2022-07-26 05:45:22 +04:00
|
|
|
} catch (const std::exception &e) {
|
2023-09-19 17:10:53 +04:00
|
|
|
LOG(("Native Notification Error: %1").arg(e.what()));
|
2022-07-26 05:45:22 +04:00
|
|
|
}
|
2022-07-31 11:10:17 +04:00
|
|
|
|
|
|
|
if (failed) {
|
|
|
|
failed();
|
|
|
|
}
|
2022-07-26 05:45:22 +04:00
|
|
|
}
|
|
|
|
|
2021-07-18 07:26:42 +04:00
|
|
|
std::unique_ptr<base::Platform::DBus::ServiceWatcher> CreateServiceWatcher() {
|
|
|
|
try {
|
|
|
|
const auto connection = Gio::DBus::Connection::get_sync(
|
2022-11-03 13:41:18 +04:00
|
|
|
Gio::DBus::BusType::SESSION);
|
2021-07-18 07:26:42 +04:00
|
|
|
|
|
|
|
const auto activatable = [&] {
|
|
|
|
try {
|
|
|
|
return ranges::contains(
|
|
|
|
base::Platform::DBus::ListActivatableNames(connection),
|
2023-07-20 18:06:45 +04:00
|
|
|
kService,
|
2022-12-07 01:27:36 +04:00
|
|
|
&Glib::ustring::raw);
|
2021-07-18 07:26:42 +04:00
|
|
|
} catch (...) {
|
|
|
|
// avoid service restart loop in sandboxed environments
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}();
|
|
|
|
|
|
|
|
return std::make_unique<base::Platform::DBus::ServiceWatcher>(
|
|
|
|
connection,
|
2023-07-20 18:06:45 +04:00
|
|
|
kService,
|
2021-07-18 07:26:42 +04:00
|
|
|
[=](
|
|
|
|
const Glib::ustring &service,
|
|
|
|
const Glib::ustring &oldOwner,
|
|
|
|
const Glib::ustring &newOwner) {
|
2023-09-04 00:30:48 +04:00
|
|
|
Core::Sandbox::Instance().customEnterFromEventLoop([&] {
|
|
|
|
if (activatable && newOwner.empty()) {
|
2022-02-28 22:52:51 +01:00
|
|
|
Core::App().notifications().clearAll();
|
2023-09-04 00:30:48 +04:00
|
|
|
} else {
|
|
|
|
Core::App().notifications().createManager();
|
|
|
|
}
|
2021-07-18 07:26:42 +04:00
|
|
|
});
|
|
|
|
});
|
|
|
|
} catch (...) {
|
|
|
|
}
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2021-05-02 10:30:47 +04:00
|
|
|
void StartServiceAsync(Fn<void()> callback) {
|
2021-02-23 06:34:01 +04:00
|
|
|
try {
|
|
|
|
const auto connection = Gio::DBus::Connection::get_sync(
|
2022-11-03 13:41:18 +04:00
|
|
|
Gio::DBus::BusType::SESSION);
|
2021-02-23 06:34:01 +04:00
|
|
|
|
2023-05-20 18:20:10 +04:00
|
|
|
base::Platform::DBus::StartServiceByNameAsync(
|
2021-02-23 06:34:01 +04:00
|
|
|
connection,
|
2023-07-20 18:06:45 +04:00
|
|
|
kService,
|
2023-05-20 18:20:10 +04:00
|
|
|
[=](Fn<base::Platform::DBus::StartReply()> result) {
|
2023-09-04 00:30:48 +04:00
|
|
|
Core::Sandbox::Instance().customEnterFromEventLoop([&] {
|
|
|
|
Noexcept([&] {
|
|
|
|
try {
|
|
|
|
result(); // get the error if any
|
|
|
|
} catch (const Glib::Error &e) {
|
|
|
|
static const auto NotSupportedErrors = {
|
|
|
|
"org.freedesktop.DBus.Error.ServiceUnknown",
|
|
|
|
};
|
|
|
|
|
|
|
|
const auto errorName =
|
2023-10-02 15:47:15 +04:00
|
|
|
Gio::DBus::ErrorUtils::get_remote_error(e)
|
|
|
|
.raw();
|
2023-09-04 00:30:48 +04:00
|
|
|
|
|
|
|
if (!ranges::contains(
|
|
|
|
NotSupportedErrors,
|
|
|
|
errorName)) {
|
2023-10-04 02:27:35 +04:00
|
|
|
throw;
|
2023-09-04 00:30:48 +04:00
|
|
|
}
|
2022-07-26 05:45:22 +04:00
|
|
|
}
|
2023-09-04 00:30:48 +04:00
|
|
|
});
|
2021-02-23 06:34:01 +04:00
|
|
|
|
2023-09-04 00:30:48 +04:00
|
|
|
callback();
|
|
|
|
});
|
2021-05-02 10:30:47 +04:00
|
|
|
});
|
2021-02-23 06:34:01 +04:00
|
|
|
|
|
|
|
return;
|
2021-04-27 16:17:10 +04:00
|
|
|
} catch (...) {
|
2021-02-23 06:34:01 +04:00
|
|
|
}
|
|
|
|
|
2023-09-04 00:30:48 +04:00
|
|
|
callback();
|
2021-02-23 06:34:01 +04:00
|
|
|
}
|
|
|
|
|
2021-01-09 06:10:06 +04:00
|
|
|
bool GetServiceRegistered() {
|
2021-02-28 06:34:41 +04:00
|
|
|
try {
|
|
|
|
const auto connection = Gio::DBus::Connection::get_sync(
|
2022-11-03 13:41:18 +04:00
|
|
|
Gio::DBus::BusType::SESSION);
|
2021-02-28 06:34:41 +04:00
|
|
|
|
2021-03-10 11:05:03 +04:00
|
|
|
const auto hasOwner = [&] {
|
|
|
|
try {
|
2023-05-20 18:20:10 +04:00
|
|
|
return base::Platform::DBus::NameHasOwner(
|
2021-03-10 11:05:03 +04:00
|
|
|
connection,
|
2023-07-20 18:06:45 +04:00
|
|
|
kService);
|
2021-03-10 11:05:03 +04:00
|
|
|
} catch (...) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}();
|
|
|
|
|
|
|
|
static const auto activatable = [&] {
|
|
|
|
try {
|
|
|
|
return ranges::contains(
|
2023-05-20 18:20:10 +04:00
|
|
|
base::Platform::DBus::ListActivatableNames(connection),
|
2023-07-20 18:06:45 +04:00
|
|
|
kService,
|
2022-12-07 01:27:36 +04:00
|
|
|
&Glib::ustring::raw);
|
2021-03-10 11:05:03 +04:00
|
|
|
} catch (...) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}();
|
|
|
|
|
|
|
|
return hasOwner || activatable;
|
2021-02-28 06:34:41 +04:00
|
|
|
} catch (...) {
|
|
|
|
}
|
2020-04-28 08:32:24 +04:00
|
|
|
|
2021-02-28 06:34:41 +04:00
|
|
|
return false;
|
2020-04-28 08:32:24 +04:00
|
|
|
}
|
|
|
|
|
2023-09-19 15:25:30 +04:00
|
|
|
void GetServerInformation(Fn<void(const ServerInformation &)> callback) {
|
2022-07-26 05:45:22 +04:00
|
|
|
Noexcept([&] {
|
2021-03-02 14:20:51 +04:00
|
|
|
const auto connection = Gio::DBus::Connection::get_sync(
|
2022-11-03 13:41:18 +04:00
|
|
|
Gio::DBus::BusType::SESSION);
|
2019-12-29 15:41:45 +00:00
|
|
|
|
2021-03-02 14:20:51 +04:00
|
|
|
connection->call(
|
2023-07-20 18:06:45 +04:00
|
|
|
kObjectPath,
|
|
|
|
kInterface,
|
2021-03-02 14:20:51 +04:00
|
|
|
"GetServerInformation",
|
|
|
|
{},
|
|
|
|
[=](const Glib::RefPtr<Gio::AsyncResult> &result) {
|
2023-09-04 00:30:48 +04:00
|
|
|
Core::Sandbox::Instance().customEnterFromEventLoop([&] {
|
|
|
|
Noexcept([&] {
|
|
|
|
const auto reply = connection->call_finish(result);
|
2021-03-02 14:20:51 +04:00
|
|
|
|
2023-10-02 15:47:15 +04:00
|
|
|
const auto name = reply
|
|
|
|
.get_child(0)
|
|
|
|
.get_dynamic<Glib::ustring>();
|
2021-03-02 14:20:51 +04:00
|
|
|
|
2023-10-02 15:47:15 +04:00
|
|
|
const auto vendor = reply
|
|
|
|
.get_child(1)
|
|
|
|
.get_dynamic<Glib::ustring>();
|
2021-03-02 14:20:51 +04:00
|
|
|
|
2023-10-02 15:47:15 +04:00
|
|
|
const auto version = reply
|
|
|
|
.get_child(2)
|
|
|
|
.get_dynamic<Glib::ustring>();
|
2021-03-02 14:20:51 +04:00
|
|
|
|
2023-10-02 15:47:15 +04:00
|
|
|
const auto specVersion = reply
|
|
|
|
.get_child(3)
|
|
|
|
.get_dynamic<Glib::ustring>();
|
2021-03-02 14:20:51 +04:00
|
|
|
|
|
|
|
callback(ServerInformation{
|
2023-09-19 13:41:50 +04:00
|
|
|
name,
|
|
|
|
vendor,
|
2021-03-02 14:20:51 +04:00
|
|
|
QVersionNumber::fromString(
|
|
|
|
QString::fromStdString(version)),
|
|
|
|
QVersionNumber::fromString(
|
|
|
|
QString::fromStdString(specVersion)),
|
|
|
|
});
|
2023-09-04 00:30:48 +04:00
|
|
|
}, [&] {
|
2023-09-19 15:25:30 +04:00
|
|
|
callback({});
|
2021-03-02 14:20:51 +04:00
|
|
|
});
|
2022-07-26 05:45:22 +04:00
|
|
|
});
|
2021-03-02 14:20:51 +04:00
|
|
|
},
|
2023-07-20 18:06:45 +04:00
|
|
|
kService);
|
2022-07-31 11:10:17 +04:00
|
|
|
}, [&] {
|
2023-09-19 15:25:30 +04:00
|
|
|
callback({});
|
2022-07-26 05:45:22 +04:00
|
|
|
});
|
2016-10-03 18:07:50 +03:00
|
|
|
}
|
|
|
|
|
2023-09-19 13:41:50 +04:00
|
|
|
void GetCapabilities(Fn<void(const std::vector<Glib::ustring> &)> callback) {
|
2022-07-26 05:45:22 +04:00
|
|
|
Noexcept([&] {
|
2021-03-02 14:20:51 +04:00
|
|
|
const auto connection = Gio::DBus::Connection::get_sync(
|
2022-11-03 13:41:18 +04:00
|
|
|
Gio::DBus::BusType::SESSION);
|
2018-11-08 13:58:04 +03:00
|
|
|
|
2021-03-02 14:20:51 +04:00
|
|
|
connection->call(
|
2023-07-20 18:06:45 +04:00
|
|
|
kObjectPath,
|
|
|
|
kInterface,
|
2021-03-02 14:20:51 +04:00
|
|
|
"GetCapabilities",
|
|
|
|
{},
|
|
|
|
[=](const Glib::RefPtr<Gio::AsyncResult> &result) {
|
2023-09-04 00:30:48 +04:00
|
|
|
Core::Sandbox::Instance().customEnterFromEventLoop([&] {
|
|
|
|
Noexcept([&] {
|
2023-09-19 13:41:50 +04:00
|
|
|
callback(
|
2023-10-02 15:47:15 +04:00
|
|
|
connection->call_finish(result)
|
|
|
|
.get_child(0)
|
|
|
|
.get_dynamic<std::vector<Glib::ustring>>()
|
2023-09-19 13:41:50 +04:00
|
|
|
);
|
2023-09-04 00:30:48 +04:00
|
|
|
}, [&] {
|
|
|
|
callback({});
|
|
|
|
});
|
2022-07-26 05:45:22 +04:00
|
|
|
});
|
2021-03-02 14:20:51 +04:00
|
|
|
},
|
2023-07-20 18:06:45 +04:00
|
|
|
kService);
|
2022-07-31 11:10:17 +04:00
|
|
|
}, [&] {
|
2023-09-04 00:30:48 +04:00
|
|
|
callback({});
|
2022-07-26 05:45:22 +04:00
|
|
|
});
|
2019-12-29 15:41:45 +00:00
|
|
|
}
|
2016-10-03 18:07:50 +03:00
|
|
|
|
2022-07-26 05:32:33 +04:00
|
|
|
void GetInhibited(Fn<void(bool)> callback) {
|
2022-07-26 05:45:22 +04:00
|
|
|
Noexcept([&] {
|
2021-03-02 14:20:51 +04:00
|
|
|
const auto connection = Gio::DBus::Connection::get_sync(
|
2022-11-03 13:41:18 +04:00
|
|
|
Gio::DBus::BusType::SESSION);
|
2021-01-13 13:56:49 +04:00
|
|
|
|
2021-03-02 14:20:51 +04:00
|
|
|
connection->call(
|
2023-07-20 18:06:45 +04:00
|
|
|
kObjectPath,
|
|
|
|
kPropertiesInterface,
|
2021-03-02 14:20:51 +04:00
|
|
|
"Get",
|
2023-05-20 18:20:10 +04:00
|
|
|
Glib::create_variant(std::tuple{
|
2023-07-20 18:06:45 +04:00
|
|
|
Glib::ustring(kInterface),
|
2021-03-02 14:20:51 +04:00
|
|
|
Glib::ustring("Inhibited"),
|
|
|
|
}),
|
|
|
|
[=](const Glib::RefPtr<Gio::AsyncResult> &result) {
|
2023-09-04 00:30:48 +04:00
|
|
|
Core::Sandbox::Instance().customEnterFromEventLoop([&] {
|
|
|
|
Noexcept([&] {
|
2023-09-27 00:24:00 +04:00
|
|
|
callback(
|
2023-10-02 15:47:15 +04:00
|
|
|
connection->call_finish(result)
|
|
|
|
.get_child(0)
|
|
|
|
.get_dynamic<Glib::Variant<bool>>()
|
|
|
|
.get()
|
2023-09-27 00:24:00 +04:00
|
|
|
);
|
2023-09-04 00:30:48 +04:00
|
|
|
}, [&] {
|
|
|
|
callback(false);
|
|
|
|
});
|
2022-07-26 05:45:22 +04:00
|
|
|
});
|
2021-03-02 14:20:51 +04:00
|
|
|
},
|
2023-07-20 18:06:45 +04:00
|
|
|
kService);
|
2022-07-31 11:10:17 +04:00
|
|
|
}, [&] {
|
2023-09-04 00:30:48 +04:00
|
|
|
callback(false);
|
2022-07-26 05:45:22 +04:00
|
|
|
});
|
2021-01-13 13:56:49 +04:00
|
|
|
}
|
|
|
|
|
2021-02-28 06:34:41 +04:00
|
|
|
Glib::ustring GetImageKey(const QVersionNumber &specificationVersion) {
|
2021-01-16 08:47:07 +04:00
|
|
|
const auto normalizedVersion = specificationVersion.normalized();
|
|
|
|
|
|
|
|
if (normalizedVersion.isNull()) {
|
2021-01-22 16:07:31 +04:00
|
|
|
LOG(("Native Notification Error: specification version is null"));
|
2021-02-28 06:34:41 +04:00
|
|
|
return {};
|
2021-01-16 08:47:07 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (normalizedVersion >= QVersionNumber(1, 2)) {
|
2021-02-28 06:34:41 +04:00
|
|
|
return "image-data";
|
2021-01-16 08:47:07 +04:00
|
|
|
} else if (normalizedVersion == QVersionNumber(1, 1)) {
|
2021-02-28 06:34:41 +04:00
|
|
|
return "image_data";
|
2020-02-25 03:45:46 +04:00
|
|
|
}
|
2020-02-29 07:42:24 +04:00
|
|
|
|
2021-02-28 06:34:41 +04:00
|
|
|
return "icon_data";
|
2020-02-25 03:45:46 +04:00
|
|
|
}
|
|
|
|
|
2023-01-12 21:06:32 +04:00
|
|
|
bool UseGNotification() {
|
|
|
|
if (!Gio::Application::get_default()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Window::Notifications::OptionGNotification.value()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return KSandbox::isFlatpak() && !ServiceRegistered;
|
|
|
|
}
|
|
|
|
|
2021-05-03 13:08:50 +04:00
|
|
|
class NotificationData final : public base::has_weak_ptr {
|
2020-08-01 11:03:17 +00:00
|
|
|
public:
|
|
|
|
using NotificationId = Window::Notifications::Manager::NotificationId;
|
|
|
|
|
|
|
|
NotificationData(
|
2021-05-03 13:08:50 +04:00
|
|
|
not_null<Manager*> manager,
|
|
|
|
NotificationId id);
|
|
|
|
|
|
|
|
[[nodiscard]] bool init(
|
2020-08-01 11:03:17 +00:00
|
|
|
const QString &title,
|
|
|
|
const QString &subtitle,
|
|
|
|
const QString &msg,
|
2021-10-06 19:24:30 +04:00
|
|
|
Window::Notifications::Manager::DisplayOptions options);
|
2020-08-01 11:03:17 +00:00
|
|
|
|
|
|
|
NotificationData(const NotificationData &other) = delete;
|
|
|
|
NotificationData &operator=(const NotificationData &other) = delete;
|
|
|
|
NotificationData(NotificationData &&other) = delete;
|
|
|
|
NotificationData &operator=(NotificationData &&other) = delete;
|
|
|
|
|
|
|
|
~NotificationData();
|
|
|
|
|
2021-01-20 02:31:27 +04:00
|
|
|
void show();
|
2020-08-01 11:03:17 +00:00
|
|
|
void close();
|
2023-09-12 09:17:34 +04:00
|
|
|
void setImage(QImage image);
|
2020-08-01 11:03:17 +00:00
|
|
|
|
|
|
|
private:
|
2021-05-03 13:08:50 +04:00
|
|
|
const not_null<Manager*> _manager;
|
|
|
|
NotificationId _id;
|
2020-08-01 11:03:17 +00:00
|
|
|
|
2022-11-11 15:49:50 +04:00
|
|
|
Glib::RefPtr<Gio::Application> _application;
|
|
|
|
Glib::RefPtr<Gio::Notification> _notification;
|
2023-08-02 21:02:37 +04:00
|
|
|
const std::string _guid;
|
2022-11-11 15:49:50 +04:00
|
|
|
|
2021-05-03 13:08:50 +04:00
|
|
|
Glib::RefPtr<Gio::DBus::Connection> _dbusConnection;
|
2021-02-28 06:34:41 +04:00
|
|
|
Glib::ustring _title;
|
|
|
|
Glib::ustring _body;
|
|
|
|
std::vector<Glib::ustring> _actions;
|
|
|
|
std::map<Glib::ustring, Glib::VariantBase> _hints;
|
|
|
|
Glib::ustring _imageKey;
|
2020-08-01 11:03:17 +00:00
|
|
|
|
|
|
|
uint _notificationId = 0;
|
2021-02-28 06:34:41 +04:00
|
|
|
uint _actionInvokedSignalId = 0;
|
2022-06-15 12:17:06 +04:00
|
|
|
uint _activationTokenSignalId = 0;
|
2021-02-28 06:34:41 +04:00
|
|
|
uint _notificationRepliedSignalId = 0;
|
|
|
|
uint _notificationClosedSignalId = 0;
|
2020-08-01 11:03:17 +00:00
|
|
|
|
2020-10-27 14:13:28 +04:00
|
|
|
void notificationClosed(uint id, uint reason);
|
2021-02-28 06:34:41 +04:00
|
|
|
void actionInvoked(uint id, const Glib::ustring &actionName);
|
2022-06-15 12:17:06 +04:00
|
|
|
void activationToken(uint id, const Glib::ustring &token);
|
2021-02-28 06:34:41 +04:00
|
|
|
void notificationReplied(uint id, const Glib::ustring &text);
|
|
|
|
|
2020-08-01 11:03:17 +00:00
|
|
|
};
|
|
|
|
|
2021-05-03 13:08:50 +04:00
|
|
|
using Notification = std::unique_ptr<NotificationData>;
|
2016-10-03 18:07:50 +03:00
|
|
|
|
2019-12-29 15:41:45 +00:00
|
|
|
NotificationData::NotificationData(
|
2021-05-03 13:08:50 +04:00
|
|
|
not_null<Manager*> manager,
|
|
|
|
NotificationId id)
|
2021-05-02 10:30:47 +04:00
|
|
|
: _manager(manager)
|
2022-11-11 15:49:50 +04:00
|
|
|
, _id(id)
|
2023-01-12 21:06:32 +04:00
|
|
|
, _application(UseGNotification()
|
|
|
|
? Gio::Application::get_default()
|
2023-08-02 21:02:37 +04:00
|
|
|
: nullptr)
|
|
|
|
, _guid(_application ? Gio::DBus::generate_guid() : std::string()) {
|
2021-05-03 13:08:50 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
bool NotificationData::init(
|
|
|
|
const QString &title,
|
|
|
|
const QString &subtitle,
|
|
|
|
const QString &msg,
|
2021-10-06 19:24:30 +04:00
|
|
|
Window::Notifications::Manager::DisplayOptions options) {
|
2022-11-11 15:49:50 +04:00
|
|
|
if (_application) {
|
2022-12-29 05:03:40 +04:00
|
|
|
_notification = Gio::Notification::create(
|
2022-11-11 15:49:50 +04:00
|
|
|
subtitle.isEmpty()
|
2022-12-29 05:03:40 +04:00
|
|
|
? title.toStdString()
|
|
|
|
: subtitle.toStdString() + " (" + title.toStdString() + ')');
|
|
|
|
|
|
|
|
_notification->set_body(msg.toStdString());
|
2022-11-11 15:49:50 +04:00
|
|
|
|
|
|
|
_notification->set_icon(
|
|
|
|
Gio::ThemedIcon::create(base::IconName().toStdString()));
|
|
|
|
|
2023-06-05 09:24:29 +04:00
|
|
|
// for chat messages, according to
|
|
|
|
// https://docs.gtk.org/gio/enum.NotificationPriority.html
|
|
|
|
_notification->set_priority(Gio::Notification::Priority::HIGH);
|
2022-11-11 15:49:50 +04:00
|
|
|
|
2023-06-05 09:24:29 +04:00
|
|
|
// glib 2.70+, we keep glib 2.56+ compatibility
|
2022-11-11 15:49:50 +04:00
|
|
|
static const auto set_category = [] {
|
|
|
|
// reset dlerror after dlsym call
|
|
|
|
const auto guard = gsl::finally([] { dlerror(); });
|
2023-10-06 23:08:11 +04:00
|
|
|
return reinterpret_cast<void(*)(GNotification*, const gchar*)>(
|
2022-11-11 15:49:50 +04:00
|
|
|
dlsym(RTLD_DEFAULT, "g_notification_set_category"));
|
|
|
|
}();
|
|
|
|
|
|
|
|
if (set_category) {
|
|
|
|
set_category(_notification->gobj(), "im.received");
|
|
|
|
}
|
|
|
|
|
|
|
|
const auto idTuple = _id.toTuple();
|
|
|
|
|
|
|
|
_notification->set_default_action(
|
2023-03-02 13:19:53 +04:00
|
|
|
"app.notification-activate",
|
2022-11-11 15:49:50 +04:00
|
|
|
idTuple);
|
|
|
|
|
|
|
|
if (!options.hideMarkAsRead) {
|
|
|
|
_notification->add_button(
|
|
|
|
tr::lng_context_mark_read(tr::now).toStdString(),
|
|
|
|
"app.notification-mark-as-read",
|
|
|
|
idTuple);
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-07-26 05:45:22 +04:00
|
|
|
Noexcept([&] {
|
2021-02-28 06:34:41 +04:00
|
|
|
_dbusConnection = Gio::DBus::Connection::get_sync(
|
2022-11-03 13:41:18 +04:00
|
|
|
Gio::DBus::BusType::SESSION);
|
2022-07-26 05:45:22 +04:00
|
|
|
});
|
|
|
|
|
|
|
|
if (!_dbusConnection) {
|
2021-05-03 13:08:50 +04:00
|
|
|
return false;
|
2020-10-08 20:40:32 +04:00
|
|
|
}
|
|
|
|
|
2021-05-03 13:08:50 +04:00
|
|
|
const auto weak = base::make_weak(this);
|
2019-12-29 15:41:45 +00:00
|
|
|
|
2023-09-04 00:30:48 +04:00
|
|
|
const auto signalEmitted = crl::guard(weak, [=](
|
2021-05-03 13:08:50 +04:00
|
|
|
const Glib::RefPtr<Gio::DBus::Connection> &connection,
|
|
|
|
const Glib::ustring &sender_name,
|
|
|
|
const Glib::ustring &object_path,
|
|
|
|
const Glib::ustring &interface_name,
|
|
|
|
const Glib::ustring &signal_name,
|
2023-05-20 18:20:10 +04:00
|
|
|
const Glib::VariantContainerBase ¶meters) {
|
2023-09-04 00:30:48 +04:00
|
|
|
Core::Sandbox::Instance().customEnterFromEventLoop([&] {
|
|
|
|
Noexcept([&] {
|
|
|
|
if (signal_name == "ActionInvoked") {
|
2023-10-02 15:47:15 +04:00
|
|
|
const auto id = parameters
|
|
|
|
.get_child(0)
|
|
|
|
.get_dynamic<uint>();
|
2021-05-02 10:30:47 +04:00
|
|
|
|
2023-10-02 15:47:15 +04:00
|
|
|
const auto actionName = parameters
|
|
|
|
.get_child(1)
|
|
|
|
.get_dynamic<Glib::ustring>();
|
2021-05-02 10:30:47 +04:00
|
|
|
|
2023-09-04 00:30:48 +04:00
|
|
|
actionInvoked(id, actionName);
|
|
|
|
} else if (signal_name == "ActivationToken") {
|
2023-10-02 15:47:15 +04:00
|
|
|
const auto id = parameters
|
|
|
|
.get_child(0)
|
|
|
|
.get_dynamic<uint>();
|
2022-06-15 12:17:06 +04:00
|
|
|
|
2023-10-02 15:47:15 +04:00
|
|
|
const auto token = parameters
|
|
|
|
.get_child(1)
|
|
|
|
.get_dynamic<Glib::ustring>();
|
2022-06-15 12:17:06 +04:00
|
|
|
|
2023-09-04 00:30:48 +04:00
|
|
|
activationToken(id, token);
|
|
|
|
} else if (signal_name == "NotificationReplied") {
|
2023-10-02 15:47:15 +04:00
|
|
|
const auto id = parameters
|
|
|
|
.get_child(0)
|
|
|
|
.get_dynamic<uint>();
|
2021-05-02 10:30:47 +04:00
|
|
|
|
2023-10-02 15:47:15 +04:00
|
|
|
const auto text = parameters
|
|
|
|
.get_child(1)
|
|
|
|
.get_dynamic<Glib::ustring>();
|
2021-05-02 10:30:47 +04:00
|
|
|
|
2023-09-04 00:30:48 +04:00
|
|
|
notificationReplied(id, text);
|
|
|
|
} else if (signal_name == "NotificationClosed") {
|
2023-10-02 15:47:15 +04:00
|
|
|
const auto id = parameters
|
|
|
|
.get_child(0)
|
|
|
|
.get_dynamic<uint>();
|
2021-05-02 10:30:47 +04:00
|
|
|
|
2023-10-02 15:47:15 +04:00
|
|
|
const auto reason = parameters
|
|
|
|
.get_child(1)
|
|
|
|
.get_dynamic<uint>();
|
2021-05-02 10:30:47 +04:00
|
|
|
|
2023-09-04 00:30:48 +04:00
|
|
|
notificationClosed(id, reason);
|
|
|
|
}
|
|
|
|
});
|
2022-07-26 05:45:22 +04:00
|
|
|
});
|
2023-09-04 00:30:48 +04:00
|
|
|
});
|
2021-05-03 13:08:50 +04:00
|
|
|
|
2023-09-19 15:25:30 +04:00
|
|
|
_imageKey = GetImageKey(CurrentServerInformation.specVersion);
|
2021-05-02 10:30:47 +04:00
|
|
|
|
2023-10-02 17:54:15 +04:00
|
|
|
if (HasCapability("body-markup")) {
|
2022-12-29 05:03:40 +04:00
|
|
|
_title = title.toStdString();
|
|
|
|
|
2019-12-29 15:41:45 +00:00
|
|
|
_body = subtitle.isEmpty()
|
2021-02-28 06:34:41 +04:00
|
|
|
? msg.toHtmlEscaped().toStdString()
|
2022-11-30 00:46:36 +03:00
|
|
|
: u"<b>%1</b>\n%2"_q.arg(
|
2021-03-13 15:50:34 +04:00
|
|
|
subtitle.toHtmlEscaped(),
|
|
|
|
msg.toHtmlEscaped()).toStdString();
|
2019-12-29 15:41:45 +00:00
|
|
|
} else {
|
2022-12-29 05:03:40 +04:00
|
|
|
_title = subtitle.isEmpty()
|
|
|
|
? title.toStdString()
|
|
|
|
: subtitle.toStdString() + " (" + title.toStdString() + ')';
|
|
|
|
|
|
|
|
_body = msg.toStdString();
|
2016-10-03 18:07:50 +03:00
|
|
|
}
|
|
|
|
|
2023-10-02 17:54:15 +04:00
|
|
|
if (HasCapability("actions")) {
|
2021-02-28 06:34:41 +04:00
|
|
|
_actions.push_back("default");
|
2023-03-02 13:19:53 +04:00
|
|
|
_actions.push_back(tr::lng_open_link(tr::now).toStdString());
|
2020-01-17 11:18:25 +04:00
|
|
|
|
2021-10-06 19:24:30 +04:00
|
|
|
if (!options.hideMarkAsRead) {
|
2023-03-02 13:19:53 +04:00
|
|
|
// icon name according to https://specifications.freedesktop.org/icon-naming-spec/icon-naming-spec-latest.html
|
2021-02-28 06:34:41 +04:00
|
|
|
_actions.push_back("mail-mark-read");
|
|
|
|
_actions.push_back(
|
|
|
|
tr::lng_context_mark_read(tr::now).toStdString());
|
2020-10-12 12:05:43 +04:00
|
|
|
}
|
2020-01-21 13:27:50 +01:00
|
|
|
|
2023-10-02 17:54:15 +04:00
|
|
|
if (HasCapability("inline-reply")
|
2023-03-02 13:19:53 +04:00
|
|
|
&& !options.hideReplyButton) {
|
|
|
|
_actions.push_back("inline-reply");
|
|
|
|
_actions.push_back(
|
|
|
|
tr::lng_notification_reply(tr::now).toStdString());
|
|
|
|
|
|
|
|
_notificationRepliedSignalId =
|
|
|
|
_dbusConnection->signal_subscribe(
|
|
|
|
signalEmitted,
|
2023-07-20 18:06:45 +04:00
|
|
|
kService,
|
|
|
|
kInterface,
|
2023-03-02 13:19:53 +04:00
|
|
|
"NotificationReplied",
|
2023-07-20 18:06:45 +04:00
|
|
|
kObjectPath);
|
2020-01-21 13:27:50 +01:00
|
|
|
}
|
2020-10-08 20:40:32 +04:00
|
|
|
|
2021-02-28 06:34:41 +04:00
|
|
|
_actionInvokedSignalId = _dbusConnection->signal_subscribe(
|
2021-05-02 10:30:47 +04:00
|
|
|
signalEmitted,
|
2023-07-20 18:06:45 +04:00
|
|
|
kService,
|
|
|
|
kInterface,
|
2020-10-08 20:40:32 +04:00
|
|
|
"ActionInvoked",
|
2023-07-20 18:06:45 +04:00
|
|
|
kObjectPath);
|
2022-06-15 12:17:06 +04:00
|
|
|
|
|
|
|
_activationTokenSignalId = _dbusConnection->signal_subscribe(
|
|
|
|
signalEmitted,
|
2023-07-20 18:06:45 +04:00
|
|
|
kService,
|
|
|
|
kInterface,
|
2022-06-15 12:17:06 +04:00
|
|
|
"ActivationToken",
|
2023-07-20 18:06:45 +04:00
|
|
|
kObjectPath);
|
2016-10-03 18:07:50 +03:00
|
|
|
}
|
|
|
|
|
2023-10-02 17:54:15 +04:00
|
|
|
if (HasCapability("action-icons")) {
|
2023-05-20 18:20:10 +04:00
|
|
|
_hints["action-icons"] = Glib::create_variant(true);
|
2016-10-03 18:07:50 +03:00
|
|
|
}
|
|
|
|
|
2020-08-01 11:03:17 +00:00
|
|
|
// suppress system sound if telegram sound activated,
|
|
|
|
// otherwise use system sound
|
2023-10-02 17:54:15 +04:00
|
|
|
if (HasCapability("sound")) {
|
2020-06-18 22:04:16 +04:00
|
|
|
if (Core::App().settings().soundNotify()) {
|
2023-05-20 18:20:10 +04:00
|
|
|
_hints["suppress-sound"] = Glib::create_variant(true);
|
2019-12-29 15:41:45 +00:00
|
|
|
} else {
|
|
|
|
// sound name according to http://0pointer.de/public/sound-naming-spec.html
|
2023-05-20 18:20:10 +04:00
|
|
|
_hints["sound-name"] = Glib::create_variant(
|
|
|
|
Glib::ustring("message-new-instant"));
|
2016-10-04 16:36:50 +03:00
|
|
|
}
|
|
|
|
}
|
2019-12-29 15:41:45 +00:00
|
|
|
|
2023-10-02 17:54:15 +04:00
|
|
|
if (HasCapability("x-canonical-append")) {
|
2023-05-20 18:20:10 +04:00
|
|
|
_hints["x-canonical-append"] = Glib::create_variant(
|
|
|
|
Glib::ustring("true"));
|
2016-10-03 18:07:50 +03:00
|
|
|
}
|
|
|
|
|
2023-05-20 18:20:10 +04:00
|
|
|
_hints["category"] = Glib::create_variant(Glib::ustring("im.received"));
|
2016-10-03 18:07:50 +03:00
|
|
|
|
2023-05-20 18:20:10 +04:00
|
|
|
_hints["desktop-entry"] = Glib::create_variant(
|
|
|
|
Glib::ustring(QGuiApplication::desktopFileName().toStdString()));
|
2020-08-01 11:03:17 +00:00
|
|
|
|
2021-02-28 06:34:41 +04:00
|
|
|
_notificationClosedSignalId = _dbusConnection->signal_subscribe(
|
2021-05-02 10:30:47 +04:00
|
|
|
signalEmitted,
|
2023-07-20 18:06:45 +04:00
|
|
|
kService,
|
|
|
|
kInterface,
|
2020-10-08 20:40:32 +04:00
|
|
|
"NotificationClosed",
|
2023-07-20 18:06:45 +04:00
|
|
|
kObjectPath);
|
2021-05-03 13:08:50 +04:00
|
|
|
return true;
|
2020-08-01 11:03:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
NotificationData::~NotificationData() {
|
2020-10-08 20:40:32 +04:00
|
|
|
if (_dbusConnection) {
|
|
|
|
if (_actionInvokedSignalId != 0) {
|
2021-02-28 06:34:41 +04:00
|
|
|
_dbusConnection->signal_unsubscribe(_actionInvokedSignalId);
|
2020-10-08 20:40:32 +04:00
|
|
|
}
|
|
|
|
|
2022-06-15 12:17:06 +04:00
|
|
|
if (_activationTokenSignalId != 0) {
|
|
|
|
_dbusConnection->signal_unsubscribe(_activationTokenSignalId);
|
|
|
|
}
|
|
|
|
|
2020-10-08 20:40:32 +04:00
|
|
|
if (_notificationRepliedSignalId != 0) {
|
2021-02-28 06:34:41 +04:00
|
|
|
_dbusConnection->signal_unsubscribe(_notificationRepliedSignalId);
|
2020-10-08 20:40:32 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (_notificationClosedSignalId != 0) {
|
2021-02-28 06:34:41 +04:00
|
|
|
_dbusConnection->signal_unsubscribe(_notificationClosedSignalId);
|
2020-10-08 20:40:32 +04:00
|
|
|
}
|
|
|
|
}
|
2019-12-29 15:41:45 +00:00
|
|
|
}
|
2016-10-03 18:07:50 +03:00
|
|
|
|
2021-01-20 02:31:27 +04:00
|
|
|
void NotificationData::show() {
|
2022-11-11 15:49:50 +04:00
|
|
|
if (_application && _notification) {
|
2023-08-02 21:02:37 +04:00
|
|
|
_application->send_notification(_guid, _notification);
|
2022-11-11 15:49:50 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-02-23 06:34:01 +04:00
|
|
|
// a hack for snap's activation restriction
|
2021-05-03 13:08:50 +04:00
|
|
|
const auto weak = base::make_weak(this);
|
|
|
|
StartServiceAsync(crl::guard(weak, [=] {
|
2021-02-28 06:34:41 +04:00
|
|
|
const auto iconName = _imageKey.empty()
|
|
|
|
|| _hints.find(_imageKey) == end(_hints)
|
2022-09-19 06:58:27 +04:00
|
|
|
? Glib::ustring(base::IconName().toStdString())
|
2021-02-28 06:34:41 +04:00
|
|
|
: Glib::ustring();
|
2021-05-12 12:10:55 +04:00
|
|
|
const auto connection = _dbusConnection;
|
2021-02-28 06:34:41 +04:00
|
|
|
|
2021-05-12 12:10:55 +04:00
|
|
|
connection->call(
|
2023-07-20 18:06:45 +04:00
|
|
|
kObjectPath,
|
|
|
|
kInterface,
|
2021-02-28 06:34:41 +04:00
|
|
|
"Notify",
|
2023-05-20 18:20:10 +04:00
|
|
|
Glib::create_variant(std::tuple{
|
2021-02-28 06:34:41 +04:00
|
|
|
Glib::ustring(std::string(AppName)),
|
|
|
|
uint(0),
|
|
|
|
iconName,
|
|
|
|
_title,
|
|
|
|
_body,
|
|
|
|
_actions,
|
|
|
|
_hints,
|
|
|
|
-1,
|
|
|
|
}),
|
2023-10-02 15:47:15 +04:00
|
|
|
crl::guard(weak, [=](
|
|
|
|
const Glib::RefPtr<Gio::AsyncResult> &result) {
|
2023-09-04 00:30:48 +04:00
|
|
|
Core::Sandbox::Instance().customEnterFromEventLoop([&] {
|
|
|
|
Noexcept([&] {
|
2023-10-02 15:47:15 +04:00
|
|
|
_notificationId = connection->call_finish(result)
|
|
|
|
.get_child(0)
|
|
|
|
.get_dynamic<uint>();
|
2023-09-04 00:30:48 +04:00
|
|
|
}, [&] {
|
2022-07-26 05:45:22 +04:00
|
|
|
_manager->clearNotification(_id);
|
|
|
|
});
|
2022-07-31 11:10:17 +04:00
|
|
|
});
|
2023-09-04 00:30:48 +04:00
|
|
|
}),
|
2023-07-20 18:06:45 +04:00
|
|
|
kService);
|
2021-05-02 10:30:47 +04:00
|
|
|
}));
|
2019-12-29 15:41:45 +00:00
|
|
|
}
|
|
|
|
|
2020-03-02 19:55:29 +04:00
|
|
|
void NotificationData::close() {
|
2022-11-11 15:49:50 +04:00
|
|
|
if (_application) {
|
2023-08-02 21:02:37 +04:00
|
|
|
_application->withdraw_notification(_guid);
|
2022-11-11 15:49:50 +04:00
|
|
|
_manager->clearNotification(_id);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-02-23 06:34:01 +04:00
|
|
|
_dbusConnection->call(
|
2023-07-20 18:06:45 +04:00
|
|
|
kObjectPath,
|
|
|
|
kInterface,
|
2021-02-23 06:34:01 +04:00
|
|
|
"CloseNotification",
|
2023-05-20 18:20:10 +04:00
|
|
|
Glib::create_variant(std::tuple{
|
2021-02-23 06:34:01 +04:00
|
|
|
_notificationId,
|
|
|
|
}),
|
|
|
|
{},
|
2023-07-20 18:06:45 +04:00
|
|
|
kService,
|
2022-02-28 22:52:51 +01:00
|
|
|
-1,
|
2022-11-03 13:41:18 +04:00
|
|
|
Gio::DBus::CallFlags::NO_AUTO_START);
|
2021-05-03 13:08:50 +04:00
|
|
|
_manager->clearNotification(_id);
|
2016-10-02 12:30:28 +03:00
|
|
|
}
|
|
|
|
|
2023-09-12 09:17:34 +04:00
|
|
|
void NotificationData::setImage(QImage image) {
|
2022-11-11 15:49:50 +04:00
|
|
|
if (_notification) {
|
2022-11-26 05:55:49 +04:00
|
|
|
const auto imageData = [&] {
|
2022-11-26 21:59:50 +04:00
|
|
|
QByteArray ba;
|
|
|
|
QBuffer buffer(&ba);
|
|
|
|
buffer.open(QIODevice::WriteOnly);
|
|
|
|
image.save(&buffer, "PNG");
|
|
|
|
return ba;
|
2022-11-26 05:55:49 +04:00
|
|
|
}();
|
|
|
|
|
|
|
|
_notification->set_icon(
|
2023-03-17 17:05:56 +04:00
|
|
|
Gio::BytesIcon::create(
|
|
|
|
Glib::Bytes::create(
|
|
|
|
imageData.constData(),
|
|
|
|
imageData.size())));
|
2022-11-26 05:55:49 +04:00
|
|
|
|
2022-11-11 15:49:50 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (_imageKey.empty()) {
|
2019-12-29 15:41:45 +00:00
|
|
|
return;
|
2016-10-03 18:07:50 +03:00
|
|
|
}
|
2017-03-04 22:36:59 +03:00
|
|
|
|
2023-09-12 09:17:34 +04:00
|
|
|
if (image.hasAlphaChannel()) {
|
|
|
|
image.convertTo(QImage::Format_RGBA8888);
|
|
|
|
} else {
|
|
|
|
image.convertTo(QImage::Format_RGB888);
|
|
|
|
}
|
2021-07-18 23:07:11 +04:00
|
|
|
|
2023-05-20 18:20:10 +04:00
|
|
|
_hints[_imageKey] = Glib::create_variant(std::tuple{
|
2023-09-12 09:17:34 +04:00
|
|
|
image.width(),
|
|
|
|
image.height(),
|
|
|
|
int(image.bytesPerLine()),
|
|
|
|
image.hasAlphaChannel(),
|
2020-08-01 11:03:17 +00:00
|
|
|
8,
|
2023-09-12 09:17:34 +04:00
|
|
|
image.hasAlphaChannel() ? 4 : 3,
|
2021-02-28 06:34:41 +04:00
|
|
|
std::vector<uchar>(
|
2023-09-12 09:17:34 +04:00
|
|
|
image.constBits(),
|
|
|
|
image.constBits() + image.sizeInBytes()),
|
2021-02-28 06:34:41 +04:00
|
|
|
});
|
2020-08-01 11:03:17 +00:00
|
|
|
}
|
2019-12-29 15:41:45 +00:00
|
|
|
|
2020-08-01 11:03:17 +00:00
|
|
|
void NotificationData::notificationClosed(uint id, uint reason) {
|
Do not remove notification reference on NotificationClosed
In general we need to keep a reference to the notification id, so that
we can delete the notification later from history - unless the
NotificationClosed reason was that the user actively dismissed it, in
which case it is not kept in history anyway (so we can dismiss our
reference too).
-- Background --
Some desktop environments such as KDE keep a history of notifications.
An API is provided to delete notifications from that history by calling
the org.freedesktop.Notifications.CloseNotification endpoint with the ID
of that notification. If the notification was already closed (timed
out), then this will delete the notification from history.
The intent is to clear these notifications from the notification history
as soon as a chat with notifications originating from that person is
opened, as the user is then not interested anymore in those
notifications and to prevent unnecessary clutter in the history widget.
It is also cleared when the chat is read on another device.
-- Problem --
Telegram already has all the code in place to support this
functionality, but unfortunately this did not work on Linux before,
because we listen to the NotificationClosed signal and remove our
reference to the notification id from our internal manager as soon as we
get that signal. This means that we do not clear that notification from
history once we open the chat with that person (unless we open the chat
before the notification has timed out, i.e. if we didn't get the
NotificationClosed signal).
-- Fix --
To fix this, we keep our notification reference (if the notification was
not dismissed by the user), which means that our reference will be kept
around until we open the chat with that person (or close Telegram
entirely).
Since all the needed functionality for deleting notifications was
already in place, this patch is quite short as we only need to keep the
reference around longer than we did before this patch.
Note also that code is already in place to clear notifications for
messages that were read on another device: History::inboxRead() calls
Core::App().notifications().clearIncomingFromHistory()
Fixes #17111
2022-03-04 13:46:13 +01:00
|
|
|
/*
|
|
|
|
* From: https://specifications.freedesktop.org/notification-spec/latest/ar01s09.html
|
|
|
|
* The reason the notification was closed
|
|
|
|
* 1 - The notification expired.
|
|
|
|
* 2 - The notification was dismissed by the user.
|
|
|
|
* 3 - The notification was closed by a call to CloseNotification.
|
|
|
|
* 4 - Undefined/reserved reasons.
|
|
|
|
*
|
|
|
|
* If the notification was dismissed by the user (reason == 2), the notification is not kept in notification history.
|
|
|
|
* We do not need to send a "CloseNotification" call later to clear it from history.
|
|
|
|
* Therefore we can drop the notification reference now.
|
|
|
|
* In all other cases we keep the notification reference so that we may clear the notification later from history,
|
|
|
|
* if the message for that notification is read (e.g. chat is opened or read from another device).
|
|
|
|
*/
|
|
|
|
if (id == _notificationId && reason == 2) {
|
2021-05-03 13:08:50 +04:00
|
|
|
_manager->clearNotification(_id);
|
2017-03-04 22:36:59 +03:00
|
|
|
}
|
2016-10-02 12:30:28 +03:00
|
|
|
}
|
|
|
|
|
2021-02-28 06:34:41 +04:00
|
|
|
void NotificationData::actionInvoked(
|
|
|
|
uint id,
|
|
|
|
const Glib::ustring &actionName) {
|
2020-02-06 10:49:22 +01:00
|
|
|
if (id != _notificationId) {
|
|
|
|
return;
|
2016-10-03 11:56:03 +03:00
|
|
|
}
|
2020-02-06 10:49:22 +01:00
|
|
|
|
2023-03-02 13:19:53 +04:00
|
|
|
if (actionName == "default") {
|
2021-05-03 13:08:50 +04:00
|
|
|
_manager->notificationActivated(_id);
|
2021-02-28 06:34:41 +04:00
|
|
|
} else if (actionName == "mail-mark-read") {
|
2021-05-03 13:08:50 +04:00
|
|
|
_manager->notificationReplied(_id, {});
|
2020-02-06 10:49:22 +01:00
|
|
|
}
|
2016-10-03 11:56:03 +03:00
|
|
|
}
|
|
|
|
|
2022-06-15 12:17:06 +04:00
|
|
|
void NotificationData::activationToken(uint id, const Glib::ustring &token) {
|
|
|
|
if (id == _notificationId) {
|
|
|
|
qputenv("XDG_ACTIVATION_TOKEN", QByteArray::fromStdString(token));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-28 06:34:41 +04:00
|
|
|
void NotificationData::notificationReplied(
|
|
|
|
uint id,
|
|
|
|
const Glib::ustring &text) {
|
2020-01-21 13:27:50 +01:00
|
|
|
if (id == _notificationId) {
|
2021-05-03 13:08:50 +04:00
|
|
|
_manager->notificationReplied(
|
|
|
|
_id,
|
|
|
|
{ QString::fromStdString(text), {} });
|
2020-01-21 13:27:50 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-01 11:03:17 +00:00
|
|
|
} // namespace
|
2016-10-03 18:07:50 +03:00
|
|
|
|
2023-03-15 15:00:20 +04:00
|
|
|
bool SkipToastForCustom() {
|
2021-04-28 11:20:39 +04:00
|
|
|
return false;
|
2020-03-03 12:52:05 +04:00
|
|
|
}
|
|
|
|
|
2023-03-15 15:00:20 +04:00
|
|
|
void MaybePlaySoundForCustom(Fn<void()> playSound) {
|
|
|
|
playSound();
|
2020-05-12 14:04:53 +04:00
|
|
|
}
|
2020-03-03 12:52:05 +04:00
|
|
|
|
2023-03-15 15:00:20 +04:00
|
|
|
void MaybeFlashBounceForCustom(Fn<void()> flashBounce) {
|
|
|
|
flashBounce();
|
2020-03-03 12:52:05 +04:00
|
|
|
}
|
|
|
|
|
2022-12-16 13:53:19 +04:00
|
|
|
bool WaitForInputForCustom() {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-12-29 15:41:45 +00:00
|
|
|
bool Supported() {
|
2023-01-12 21:06:32 +04:00
|
|
|
return ServiceRegistered || UseGNotification();
|
2019-12-29 15:41:45 +00:00
|
|
|
}
|
2016-10-03 11:56:03 +03:00
|
|
|
|
2021-01-13 13:56:49 +04:00
|
|
|
bool Enforced() {
|
|
|
|
// Wayland doesn't support positioning
|
|
|
|
// and custom notifications don't work here
|
2023-02-18 18:45:11 +04:00
|
|
|
return IsWayland()
|
|
|
|
|| (Gio::Application::get_default()
|
|
|
|
&& Window::Notifications::OptionGNotification.value());
|
2021-01-25 03:16:32 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
bool ByDefault() {
|
2023-01-13 10:13:40 +04:00
|
|
|
// The capabilities are static, equivalent to 'body' and 'actions' only
|
|
|
|
if (UseGNotification()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2022-07-26 05:47:30 +04:00
|
|
|
// A list of capabilities that offer feature parity
|
|
|
|
// with custom notifications
|
2023-10-02 10:44:27 -04:00
|
|
|
return ranges::all_of(std::array{
|
2022-07-26 05:47:30 +04:00
|
|
|
// To show message content
|
2023-09-19 13:41:50 +04:00
|
|
|
"body",
|
2022-07-26 05:47:30 +04:00
|
|
|
// To have buttons on notifications
|
2023-09-19 13:41:50 +04:00
|
|
|
"actions",
|
2022-07-26 05:47:30 +04:00
|
|
|
// To have quick reply
|
2023-09-19 13:41:50 +04:00
|
|
|
"inline-reply",
|
2022-07-26 05:47:30 +04:00
|
|
|
// To not to play sound with Don't Disturb activated
|
|
|
|
// (no, using sound capability is not a way)
|
2023-09-19 13:41:50 +04:00
|
|
|
"inhibitions",
|
|
|
|
}, [](const auto *capability) {
|
2023-10-02 17:54:15 +04:00
|
|
|
return HasCapability(capability);
|
2022-07-26 05:47:30 +04:00
|
|
|
});
|
2021-01-13 13:56:49 +04:00
|
|
|
}
|
|
|
|
|
2021-01-21 14:18:40 +04:00
|
|
|
void Create(Window::Notifications::System *system) {
|
2023-01-12 21:06:32 +04:00
|
|
|
static const auto ServiceWatcher = CreateServiceWatcher();
|
2022-11-26 02:36:28 +04:00
|
|
|
|
2021-01-21 14:18:40 +04:00
|
|
|
const auto managerSetter = [=] {
|
|
|
|
using ManagerType = Window::Notifications::ManagerType;
|
2022-07-26 05:55:18 +04:00
|
|
|
if ((Core::App().settings().nativeNotifications() || Enforced())
|
|
|
|
&& Supported()) {
|
2022-11-11 15:49:50 +04:00
|
|
|
if (system->manager().type() != ManagerType::Native) {
|
2021-01-21 14:18:40 +04:00
|
|
|
system->setManager(std::make_unique<Manager>(system));
|
|
|
|
}
|
2022-07-26 05:55:18 +04:00
|
|
|
} else if (Enforced()) {
|
2022-11-11 15:49:50 +04:00
|
|
|
if (system->manager().type() != ManagerType::Dummy) {
|
2022-07-26 05:55:18 +04:00
|
|
|
using DummyManager = Window::Notifications::DummyManager;
|
|
|
|
system->setManager(std::make_unique<DummyManager>(system));
|
|
|
|
}
|
2022-11-11 15:49:50 +04:00
|
|
|
} else if (system->manager().type() != ManagerType::Default) {
|
2021-02-09 15:26:33 +04:00
|
|
|
system->setManager(nullptr);
|
2021-01-21 14:18:40 +04:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2022-07-26 05:32:33 +04:00
|
|
|
const auto counter = std::make_shared<int>(2);
|
2021-01-25 01:00:01 +04:00
|
|
|
const auto oneReady = [=] {
|
|
|
|
if (!--*counter) {
|
|
|
|
managerSetter();
|
|
|
|
}
|
|
|
|
};
|
2021-01-21 14:18:40 +04:00
|
|
|
|
2022-07-26 06:54:18 +04:00
|
|
|
// snap doesn't allow access when the daemon is not running :(
|
|
|
|
StartServiceAsync([=] {
|
2021-02-23 06:34:01 +04:00
|
|
|
ServiceRegistered = GetServiceRegistered();
|
2021-01-21 14:18:40 +04:00
|
|
|
|
2021-02-23 06:34:01 +04:00
|
|
|
if (!ServiceRegistered) {
|
2023-09-19 15:25:30 +04:00
|
|
|
CurrentServerInformation = {};
|
2023-09-19 13:41:50 +04:00
|
|
|
CurrentCapabilities = {};
|
2021-02-23 06:34:01 +04:00
|
|
|
managerSetter();
|
|
|
|
return;
|
|
|
|
}
|
2021-01-21 14:18:40 +04:00
|
|
|
|
2023-09-19 15:25:30 +04:00
|
|
|
GetServerInformation([=](const ServerInformation &result) {
|
2021-02-23 06:34:01 +04:00
|
|
|
CurrentServerInformation = result;
|
|
|
|
oneReady();
|
|
|
|
});
|
|
|
|
|
2023-09-19 13:41:50 +04:00
|
|
|
GetCapabilities([=](const std::vector<Glib::ustring> &result) {
|
2021-02-23 06:34:01 +04:00
|
|
|
CurrentCapabilities = result;
|
|
|
|
oneReady();
|
|
|
|
});
|
2022-07-26 06:54:18 +04:00
|
|
|
});
|
2019-12-29 15:41:45 +00:00
|
|
|
}
|
2017-03-04 22:36:59 +03:00
|
|
|
|
2022-07-27 23:03:37 +04:00
|
|
|
class Manager::Private : public base::has_weak_ptr {
|
2020-06-22 05:28:23 +04:00
|
|
|
public:
|
2022-11-26 21:59:50 +04:00
|
|
|
explicit Private(not_null<Manager*> manager);
|
2020-06-22 05:28:23 +04:00
|
|
|
|
|
|
|
void showNotification(
|
|
|
|
not_null<PeerData*> peer,
|
2022-11-02 08:51:03 +04:00
|
|
|
MsgId topicRootId,
|
2022-12-05 16:18:10 +04:00
|
|
|
Ui::PeerUserpicView &userpicView,
|
2020-06-22 05:28:23 +04:00
|
|
|
MsgId msgId,
|
|
|
|
const QString &title,
|
|
|
|
const QString &subtitle,
|
|
|
|
const QString &msg,
|
2021-10-06 19:24:30 +04:00
|
|
|
DisplayOptions options);
|
2020-06-22 05:28:23 +04:00
|
|
|
void clearAll();
|
2021-10-15 17:57:27 +04:00
|
|
|
void clearFromItem(not_null<HistoryItem*> item);
|
2022-10-13 00:23:14 +04:00
|
|
|
void clearFromTopic(not_null<Data::ForumTopic*> topic);
|
2020-06-22 05:28:23 +04:00
|
|
|
void clearFromHistory(not_null<History*> history);
|
|
|
|
void clearFromSession(not_null<Main::Session*> session);
|
|
|
|
void clearNotification(NotificationId id);
|
2023-03-15 15:00:20 +04:00
|
|
|
void invokeIfNotInhibited(Fn<void()> callback);
|
2022-07-26 05:32:33 +04:00
|
|
|
|
2020-06-22 05:28:23 +04:00
|
|
|
~Private();
|
|
|
|
|
|
|
|
private:
|
2021-05-03 13:08:50 +04:00
|
|
|
const not_null<Manager*> _manager;
|
|
|
|
|
2020-06-22 05:28:23 +04:00
|
|
|
base::flat_map<
|
2022-10-13 00:23:14 +04:00
|
|
|
ContextId,
|
2020-06-22 05:28:23 +04:00
|
|
|
base::flat_map<MsgId, Notification>> _notifications;
|
|
|
|
|
2022-07-26 05:32:33 +04:00
|
|
|
Glib::RefPtr<Gio::DBus::Connection> _dbusConnection;
|
|
|
|
bool _inhibited = false;
|
|
|
|
uint _inhibitedSignalId = 0;
|
|
|
|
|
2020-06-22 05:28:23 +04:00
|
|
|
};
|
|
|
|
|
2022-11-26 21:59:50 +04:00
|
|
|
Manager::Private::Private(not_null<Manager*> manager)
|
|
|
|
: _manager(manager) {
|
2023-09-19 13:47:13 +04:00
|
|
|
const auto &serverInformation = CurrentServerInformation;
|
2016-10-04 16:36:50 +03:00
|
|
|
|
2023-09-19 15:25:30 +04:00
|
|
|
if (!serverInformation.name.empty()) {
|
2020-03-10 12:36:06 +04:00
|
|
|
LOG(("Notification daemon product name: %1")
|
2023-09-19 15:25:30 +04:00
|
|
|
.arg(serverInformation.name.c_str()));
|
|
|
|
}
|
2020-03-10 12:36:06 +04:00
|
|
|
|
2023-09-19 15:25:30 +04:00
|
|
|
if (!serverInformation.vendor.empty()) {
|
2020-03-10 12:36:06 +04:00
|
|
|
LOG(("Notification daemon vendor name: %1")
|
2023-09-19 15:25:30 +04:00
|
|
|
.arg(serverInformation.vendor.c_str()));
|
|
|
|
}
|
2020-03-10 12:36:06 +04:00
|
|
|
|
2023-09-19 15:25:30 +04:00
|
|
|
if (!serverInformation.version.isNull()) {
|
2020-03-10 12:36:06 +04:00
|
|
|
LOG(("Notification daemon version: %1")
|
2023-09-19 15:25:30 +04:00
|
|
|
.arg(serverInformation.version.toString()));
|
|
|
|
}
|
2020-03-10 12:36:06 +04:00
|
|
|
|
2023-09-19 15:25:30 +04:00
|
|
|
if (!serverInformation.specVersion.isNull()) {
|
2020-03-10 12:36:06 +04:00
|
|
|
LOG(("Notification daemon specification version: %1")
|
2023-09-19 15:25:30 +04:00
|
|
|
.arg(serverInformation.specVersion.toString()));
|
2016-10-03 11:56:03 +03:00
|
|
|
}
|
|
|
|
|
2023-10-02 17:54:15 +04:00
|
|
|
if (!CurrentCapabilities.empty()) {
|
2023-09-19 13:41:50 +04:00
|
|
|
LOG(("Notification daemon capabilities: %1").arg(
|
|
|
|
ranges::fold_left(
|
2023-10-02 17:54:15 +04:00
|
|
|
CurrentCapabilities,
|
2023-09-19 13:41:50 +04:00
|
|
|
"",
|
|
|
|
[](const Glib::ustring &a, const Glib::ustring &b) {
|
|
|
|
return a + (a.empty() ? "" : ", ") + b;
|
|
|
|
}).c_str()));
|
2019-12-29 15:41:45 +00:00
|
|
|
}
|
2022-07-26 05:32:33 +04:00
|
|
|
|
2023-10-02 17:54:15 +04:00
|
|
|
if (HasCapability("inhibitions")) {
|
2022-11-12 06:32:43 +04:00
|
|
|
Noexcept([&] {
|
|
|
|
_dbusConnection = Gio::DBus::Connection::get_sync(
|
|
|
|
Gio::DBus::BusType::SESSION);
|
|
|
|
});
|
2022-07-26 05:45:22 +04:00
|
|
|
|
2022-11-12 06:32:43 +04:00
|
|
|
if (!_dbusConnection) {
|
|
|
|
return;
|
|
|
|
}
|
2022-07-26 05:32:33 +04:00
|
|
|
|
2022-11-12 06:32:43 +04:00
|
|
|
const auto weak = base::make_weak(this);
|
|
|
|
GetInhibited(crl::guard(weak, [=](bool result) {
|
|
|
|
_inhibited = result;
|
|
|
|
}));
|
2022-07-26 05:32:33 +04:00
|
|
|
|
2022-11-12 06:32:43 +04:00
|
|
|
_inhibitedSignalId = _dbusConnection->signal_subscribe(
|
2023-09-04 00:30:48 +04:00
|
|
|
crl::guard(weak, [=](
|
2022-11-12 06:32:43 +04:00
|
|
|
const Glib::RefPtr<Gio::DBus::Connection> &connection,
|
|
|
|
const Glib::ustring &sender_name,
|
|
|
|
const Glib::ustring &object_path,
|
|
|
|
const Glib::ustring &interface_name,
|
|
|
|
const Glib::ustring &signal_name,
|
2023-05-20 18:20:10 +04:00
|
|
|
const Glib::VariantContainerBase ¶meters) {
|
2023-09-04 00:30:48 +04:00
|
|
|
Core::Sandbox::Instance().customEnterFromEventLoop([&] {
|
|
|
|
Noexcept([&] {
|
2023-10-02 15:47:15 +04:00
|
|
|
const auto interface = parameters
|
|
|
|
.get_child(0)
|
|
|
|
.get_dynamic<Glib::ustring>();
|
2022-07-26 05:32:33 +04:00
|
|
|
|
2023-09-04 00:30:48 +04:00
|
|
|
if (interface != kInterface) {
|
|
|
|
return;
|
|
|
|
}
|
2022-07-27 23:03:37 +04:00
|
|
|
|
2023-10-02 15:47:15 +04:00
|
|
|
_inhibited = parameters
|
|
|
|
.get_child(1)
|
|
|
|
.get_dynamic<PropertyMap>()
|
|
|
|
.at("Inhibited")
|
|
|
|
.get_dynamic<bool>();
|
2022-11-12 06:32:43 +04:00
|
|
|
});
|
2022-07-27 23:03:37 +04:00
|
|
|
});
|
2023-09-04 00:30:48 +04:00
|
|
|
}),
|
2023-07-20 18:06:45 +04:00
|
|
|
kService,
|
|
|
|
kPropertiesInterface,
|
2022-11-12 06:32:43 +04:00
|
|
|
"PropertiesChanged",
|
2023-07-20 18:06:45 +04:00
|
|
|
kObjectPath);
|
2022-11-12 06:32:43 +04:00
|
|
|
}
|
2016-10-22 16:57:13 +03:00
|
|
|
}
|
|
|
|
|
2019-08-28 17:24:12 +03:00
|
|
|
void Manager::Private::showNotification(
|
|
|
|
not_null<PeerData*> peer,
|
2022-10-13 00:23:14 +04:00
|
|
|
MsgId topicRootId,
|
2022-12-05 16:18:10 +04:00
|
|
|
Ui::PeerUserpicView &userpicView,
|
2019-08-28 17:24:12 +03:00
|
|
|
MsgId msgId,
|
|
|
|
const QString &title,
|
|
|
|
const QString &subtitle,
|
|
|
|
const QString &msg,
|
2021-10-06 19:24:30 +04:00
|
|
|
DisplayOptions options) {
|
2022-10-13 00:23:14 +04:00
|
|
|
const auto key = ContextId{
|
2020-06-24 13:05:56 +04:00
|
|
|
.sessionId = peer->session().uniqueId(),
|
2022-10-13 00:23:14 +04:00
|
|
|
.peerId = peer->id,
|
|
|
|
.topicRootId = topicRootId,
|
|
|
|
};
|
|
|
|
const auto notificationId = NotificationId{
|
|
|
|
.contextId = key,
|
|
|
|
.msgId = msgId,
|
2020-06-24 13:05:56 +04:00
|
|
|
};
|
2021-05-03 13:08:50 +04:00
|
|
|
auto notification = std::make_unique<NotificationData>(
|
2019-12-29 15:41:45 +00:00
|
|
|
_manager,
|
2021-05-03 13:08:50 +04:00
|
|
|
notificationId);
|
|
|
|
const auto inited = notification->init(
|
2019-12-29 15:41:45 +00:00
|
|
|
title,
|
|
|
|
subtitle,
|
|
|
|
msg,
|
2021-10-06 19:24:30 +04:00
|
|
|
options);
|
2021-05-03 13:08:50 +04:00
|
|
|
if (!inited) {
|
|
|
|
return;
|
|
|
|
}
|
2016-10-03 11:56:03 +03:00
|
|
|
|
2021-10-06 21:07:18 +04:00
|
|
|
if (!options.hideNameAndPhoto) {
|
2022-12-05 16:18:10 +04:00
|
|
|
notification->setImage(
|
|
|
|
Window::Notifications::GenerateUserpic(peer, userpicView));
|
2020-02-22 20:36:01 +04:00
|
|
|
}
|
2016-10-03 11:56:03 +03:00
|
|
|
|
2020-06-19 16:59:31 +04:00
|
|
|
auto i = _notifications.find(key);
|
2021-05-03 13:08:50 +04:00
|
|
|
if (i != end(_notifications)) {
|
2020-06-19 16:59:31 +04:00
|
|
|
auto j = i->second.find(msgId);
|
2021-05-03 13:08:50 +04:00
|
|
|
if (j != end(i->second)) {
|
|
|
|
auto oldNotification = std::move(j->second);
|
2020-06-19 16:59:31 +04:00
|
|
|
i->second.erase(j);
|
2016-10-03 18:07:50 +03:00
|
|
|
oldNotification->close();
|
2020-06-19 16:59:31 +04:00
|
|
|
i = _notifications.find(key);
|
2016-10-03 11:56:03 +03:00
|
|
|
}
|
|
|
|
}
|
2021-05-03 13:08:50 +04:00
|
|
|
if (i == end(_notifications)) {
|
2020-06-19 16:59:31 +04:00
|
|
|
i = _notifications.emplace(
|
|
|
|
key,
|
|
|
|
base::flat_map<MsgId, Notification>()).first;
|
2016-10-03 11:56:03 +03:00
|
|
|
}
|
2021-05-03 13:08:50 +04:00
|
|
|
const auto j = i->second.emplace(
|
|
|
|
msgId,
|
|
|
|
std::move(notification)).first;
|
|
|
|
j->second->show();
|
2016-10-03 11:56:03 +03:00
|
|
|
}
|
|
|
|
|
2017-03-04 22:36:59 +03:00
|
|
|
void Manager::Private::clearAll() {
|
2020-06-19 16:59:31 +04:00
|
|
|
for (const auto &[key, notifications] : base::take(_notifications)) {
|
|
|
|
for (const auto &[msgId, notification] : notifications) {
|
2016-10-03 18:07:50 +03:00
|
|
|
notification->close();
|
2016-10-03 11:56:03 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-15 17:57:27 +04:00
|
|
|
void Manager::Private::clearFromItem(not_null<HistoryItem*> item) {
|
2022-10-13 00:23:14 +04:00
|
|
|
const auto key = ContextId{
|
2021-10-15 17:57:27 +04:00
|
|
|
.sessionId = item->history()->session().uniqueId(),
|
2022-10-13 00:23:14 +04:00
|
|
|
.peerId = item->history()->peer->id,
|
|
|
|
.topicRootId = item->topicRootId(),
|
2021-10-15 17:57:27 +04:00
|
|
|
};
|
|
|
|
const auto i = _notifications.find(key);
|
|
|
|
if (i == _notifications.cend()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const auto j = i->second.find(item->id);
|
|
|
|
if (j == i->second.end()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const auto taken = base::take(j->second);
|
|
|
|
i->second.erase(j);
|
|
|
|
if (i->second.empty()) {
|
|
|
|
_notifications.erase(i);
|
|
|
|
}
|
|
|
|
taken->close();
|
|
|
|
}
|
|
|
|
|
2022-10-13 00:23:14 +04:00
|
|
|
void Manager::Private::clearFromTopic(not_null<Data::ForumTopic*> topic) {
|
|
|
|
const auto key = ContextId{
|
|
|
|
.sessionId = topic->session().uniqueId(),
|
|
|
|
.peerId = topic->history()->peer->id
|
2020-06-19 16:59:31 +04:00
|
|
|
};
|
2021-10-15 17:57:27 +04:00
|
|
|
const auto i = _notifications.find(key);
|
2016-10-03 11:56:03 +03:00
|
|
|
if (i != _notifications.cend()) {
|
2020-06-19 16:59:31 +04:00
|
|
|
const auto temp = base::take(i->second);
|
2016-10-03 11:56:03 +03:00
|
|
|
_notifications.erase(i);
|
|
|
|
|
2020-06-19 16:59:31 +04:00
|
|
|
for (const auto &[msgId, notification] : temp) {
|
2016-10-03 18:07:50 +03:00
|
|
|
notification->close();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-13 00:23:14 +04:00
|
|
|
void Manager::Private::clearFromHistory(not_null<History*> history) {
|
|
|
|
const auto sessionId = history->session().uniqueId();
|
|
|
|
const auto peerId = history->peer->id;
|
|
|
|
auto i = _notifications.lower_bound(ContextId{
|
|
|
|
.sessionId = sessionId,
|
|
|
|
.peerId = peerId,
|
|
|
|
});
|
|
|
|
while (i != _notifications.cend()
|
|
|
|
&& i->first.sessionId == sessionId
|
|
|
|
&& i->first.peerId == peerId) {
|
|
|
|
const auto temp = base::take(i->second);
|
|
|
|
i = _notifications.erase(i);
|
|
|
|
|
|
|
|
for (const auto &[msgId, notification] : temp) {
|
|
|
|
notification->close();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-19 16:59:31 +04:00
|
|
|
void Manager::Private::clearFromSession(not_null<Main::Session*> session) {
|
2020-06-24 13:05:56 +04:00
|
|
|
const auto sessionId = session->uniqueId();
|
2022-10-13 00:23:14 +04:00
|
|
|
auto i = _notifications.lower_bound(ContextId{
|
|
|
|
.sessionId = sessionId,
|
|
|
|
});
|
|
|
|
while (i != _notifications.cend() && i->first.sessionId == sessionId) {
|
2020-06-30 11:52:59 +04:00
|
|
|
const auto temp = base::take(i->second);
|
|
|
|
i = _notifications.erase(i);
|
2020-06-19 16:59:31 +04:00
|
|
|
|
2020-06-30 11:52:59 +04:00
|
|
|
for (const auto &[msgId, notification] : temp) {
|
|
|
|
notification->close();
|
2020-06-19 16:59:31 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Manager::Private::clearNotification(NotificationId id) {
|
2022-10-13 00:23:14 +04:00
|
|
|
auto i = _notifications.find(id.contextId);
|
2016-10-03 18:07:50 +03:00
|
|
|
if (i != _notifications.cend()) {
|
2020-06-19 16:59:31 +04:00
|
|
|
if (i->second.remove(id.msgId) && i->second.empty()) {
|
2016-10-03 18:07:50 +03:00
|
|
|
_notifications.erase(i);
|
2016-10-03 11:56:03 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-15 15:00:20 +04:00
|
|
|
void Manager::Private::invokeIfNotInhibited(Fn<void()> callback) {
|
|
|
|
if (!_inhibited) {
|
|
|
|
callback();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-04 22:36:59 +03:00
|
|
|
Manager::Private::~Private() {
|
|
|
|
clearAll();
|
2022-07-26 05:32:33 +04:00
|
|
|
|
|
|
|
if (_dbusConnection) {
|
|
|
|
if (_inhibitedSignalId != 0) {
|
|
|
|
_dbusConnection->signal_unsubscribe(_inhibitedSignalId);
|
|
|
|
}
|
|
|
|
}
|
2016-10-03 11:56:03 +03:00
|
|
|
}
|
|
|
|
|
2020-02-29 07:42:24 +04:00
|
|
|
Manager::Manager(not_null<Window::Notifications::System*> system)
|
2019-12-29 15:41:45 +00:00
|
|
|
: NativeManager(system)
|
2022-11-26 21:59:50 +04:00
|
|
|
, _private(std::make_unique<Private>(this)) {
|
2016-10-04 16:36:50 +03:00
|
|
|
}
|
|
|
|
|
2020-06-19 16:59:31 +04:00
|
|
|
void Manager::clearNotification(NotificationId id) {
|
|
|
|
_private->clearNotification(id);
|
2016-10-03 18:07:50 +03:00
|
|
|
}
|
|
|
|
|
2016-10-03 11:56:03 +03:00
|
|
|
Manager::~Manager() = default;
|
|
|
|
|
2019-08-28 17:24:12 +03:00
|
|
|
void Manager::doShowNativeNotification(
|
|
|
|
not_null<PeerData*> peer,
|
2022-11-02 08:51:03 +04:00
|
|
|
MsgId topicRootId,
|
2022-12-05 16:18:10 +04:00
|
|
|
Ui::PeerUserpicView &userpicView,
|
2019-08-28 17:24:12 +03:00
|
|
|
MsgId msgId,
|
|
|
|
const QString &title,
|
|
|
|
const QString &subtitle,
|
|
|
|
const QString &msg,
|
2021-10-06 19:24:30 +04:00
|
|
|
DisplayOptions options) {
|
2019-08-28 17:24:12 +03:00
|
|
|
_private->showNotification(
|
|
|
|
peer,
|
2022-11-02 08:51:03 +04:00
|
|
|
topicRootId,
|
2020-05-29 20:55:01 +04:00
|
|
|
userpicView,
|
2019-08-28 17:24:12 +03:00
|
|
|
msgId,
|
|
|
|
title,
|
|
|
|
subtitle,
|
|
|
|
msg,
|
2021-10-06 19:24:30 +04:00
|
|
|
options);
|
2016-10-03 11:56:03 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void Manager::doClearAllFast() {
|
2017-03-04 22:36:59 +03:00
|
|
|
_private->clearAll();
|
2016-10-03 11:56:03 +03:00
|
|
|
}
|
|
|
|
|
2021-10-15 17:57:27 +04:00
|
|
|
void Manager::doClearFromItem(not_null<HistoryItem*> item) {
|
|
|
|
_private->clearFromItem(item);
|
|
|
|
}
|
|
|
|
|
2022-10-13 00:23:14 +04:00
|
|
|
void Manager::doClearFromTopic(not_null<Data::ForumTopic*> topic) {
|
|
|
|
_private->clearFromTopic(topic);
|
|
|
|
}
|
|
|
|
|
2019-08-30 17:06:21 +03:00
|
|
|
void Manager::doClearFromHistory(not_null<History*> history) {
|
2017-03-04 22:36:59 +03:00
|
|
|
_private->clearFromHistory(history);
|
2016-10-02 12:30:28 +03:00
|
|
|
}
|
2020-06-19 16:59:31 +04:00
|
|
|
|
|
|
|
void Manager::doClearFromSession(not_null<Main::Session*> session) {
|
|
|
|
_private->clearFromSession(session);
|
|
|
|
}
|
2016-10-02 12:30:28 +03:00
|
|
|
|
2021-04-28 11:20:39 +04:00
|
|
|
bool Manager::doSkipToast() const {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-03-15 15:00:20 +04:00
|
|
|
void Manager::doMaybePlaySound(Fn<void()> playSound) {
|
|
|
|
_private->invokeIfNotInhibited(std::move(playSound));
|
|
|
|
}
|
|
|
|
|
|
|
|
void Manager::doMaybeFlashBounce(Fn<void()> flashBounce) {
|
|
|
|
_private->invokeIfNotInhibited(std::move(flashBounce));
|
2021-04-28 11:20:39 +04:00
|
|
|
}
|
|
|
|
|
2016-10-02 12:30:28 +03:00
|
|
|
} // namespace Notifications
|
|
|
|
} // namespace Platform
|