mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-15 21:57:10 +02:00
Support GNotification
It's used if there's a gtk notification daemon or application is running sandboxed without access to the freedesktop protocol. GNotification API is poor, but should feel native on environments using GNOME technologies.
This commit is contained in:
parent
afaad155a0
commit
f9dd2b4a0a
10 changed files with 439 additions and 16 deletions
|
@ -1125,6 +1125,7 @@ PRIVATE
|
|||
platform/platform_integration.h
|
||||
platform/platform_main_window.h
|
||||
platform/platform_notifications_manager.h
|
||||
platform/platform_specific.cpp
|
||||
platform/platform_specific.h
|
||||
platform/platform_tray.h
|
||||
platform/platform_window_title.h
|
||||
|
|
|
@ -9,10 +9,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "platform/linux/notifications_manager_linux.h"
|
||||
|
||||
#include "window/notifications_utilities.h"
|
||||
#include "base/options.h"
|
||||
#include "base/platform/base_platform_info.h"
|
||||
#include "base/platform/linux/base_linux_glibmm_helper.h"
|
||||
#include "base/platform/linux/base_linux_dbus_utilities.h"
|
||||
#include "platform/platform_specific.h"
|
||||
#include "core/application.h"
|
||||
#include "core/sandbox.h"
|
||||
#include "core/core_settings.h"
|
||||
#include "data/data_forum_topic.h"
|
||||
#include "history/history.h"
|
||||
|
@ -27,6 +30,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include <glibmm.h>
|
||||
#include <giomm.h>
|
||||
|
||||
#include <dlfcn.h>
|
||||
|
||||
namespace Platform {
|
||||
namespace Notifications {
|
||||
namespace {
|
||||
|
@ -341,6 +346,9 @@ private:
|
|||
const not_null<Manager*> _manager;
|
||||
NotificationId _id;
|
||||
|
||||
Glib::RefPtr<Gio::Application> _application;
|
||||
Glib::RefPtr<Gio::Notification> _notification;
|
||||
|
||||
Glib::RefPtr<Gio::DBus::Connection> _dbusConnection;
|
||||
Glib::ustring _title;
|
||||
Glib::ustring _body;
|
||||
|
@ -367,7 +375,8 @@ NotificationData::NotificationData(
|
|||
not_null<Manager*> manager,
|
||||
NotificationId id)
|
||||
: _manager(manager)
|
||||
, _id(id) {
|
||||
, _id(id)
|
||||
, _application(Gio::Application::get_default()) {
|
||||
}
|
||||
|
||||
bool NotificationData::init(
|
||||
|
@ -375,6 +384,64 @@ bool NotificationData::init(
|
|||
const QString &subtitle,
|
||||
const QString &msg,
|
||||
Window::Notifications::Manager::DisplayOptions options) {
|
||||
if (_application) {
|
||||
_notification = Gio::Notification::create(title.toStdString());
|
||||
|
||||
_notification->set_body(
|
||||
subtitle.isEmpty()
|
||||
? msg.toStdString()
|
||||
: qsl("%1\n%2").arg(subtitle, msg).toStdString());
|
||||
|
||||
_notification->set_icon(
|
||||
Gio::ThemedIcon::create(base::IconName().toStdString()));
|
||||
|
||||
// glib 2.42+, we keep glib 2.40+ compatibility
|
||||
static const auto set_priority = [] {
|
||||
// reset dlerror after dlsym call
|
||||
const auto guard = gsl::finally([] { dlerror(); });
|
||||
return reinterpret_cast<decltype(&g_notification_set_priority)>(
|
||||
dlsym(RTLD_DEFAULT, "g_notification_set_priority"));
|
||||
}();
|
||||
|
||||
if (set_priority) {
|
||||
// for chat messages, according to
|
||||
// https://docs.gtk.org/gio/enum.NotificationPriority.html
|
||||
set_priority(_notification->gobj(), G_NOTIFICATION_PRIORITY_HIGH);
|
||||
}
|
||||
|
||||
// glib 2.70+, we keep glib 2.40+ compatibility
|
||||
static const auto set_category = [] {
|
||||
// reset dlerror after dlsym call
|
||||
const auto guard = gsl::finally([] { dlerror(); });
|
||||
return reinterpret_cast<decltype(&g_notification_set_category)>(
|
||||
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(
|
||||
"app.notification-reply",
|
||||
idTuple);
|
||||
|
||||
if (!options.hideMarkAsRead) {
|
||||
_notification->add_button(
|
||||
tr::lng_context_mark_read(tr::now).toStdString(),
|
||||
"app.notification-mark-as-read",
|
||||
idTuple);
|
||||
}
|
||||
|
||||
_notification->add_button(
|
||||
tr::lng_notification_reply(tr::now).toStdString(),
|
||||
"app.notification-reply",
|
||||
idTuple);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Noexcept([&] {
|
||||
_dbusConnection = Gio::DBus::Connection::get_sync(
|
||||
Gio::DBus::BusType::SESSION);
|
||||
|
@ -545,6 +612,17 @@ NotificationData::~NotificationData() {
|
|||
}
|
||||
|
||||
void NotificationData::show() {
|
||||
if (_application && _notification) {
|
||||
_application->send_notification(
|
||||
std::to_string(_id.contextId.sessionId)
|
||||
+ '-'
|
||||
+ std::to_string(_id.contextId.peerId.value)
|
||||
+ '-'
|
||||
+ std::to_string(_id.msgId.bare),
|
||||
_notification);
|
||||
return;
|
||||
}
|
||||
|
||||
// a hack for snap's activation restriction
|
||||
const auto weak = base::make_weak(this);
|
||||
StartServiceAsync(crl::guard(weak, [=] {
|
||||
|
@ -587,6 +665,17 @@ void NotificationData::show() {
|
|||
}
|
||||
|
||||
void NotificationData::close() {
|
||||
if (_application) {
|
||||
_application->withdraw_notification(
|
||||
std::to_string(_id.contextId.sessionId)
|
||||
+ '-'
|
||||
+ std::to_string(_id.contextId.peerId.value)
|
||||
+ '-'
|
||||
+ std::to_string(_id.msgId.bare));
|
||||
_manager->clearNotification(_id);
|
||||
return;
|
||||
}
|
||||
|
||||
_dbusConnection->call(
|
||||
std::string(kObjectPath),
|
||||
std::string(kInterface),
|
||||
|
@ -602,7 +691,16 @@ void NotificationData::close() {
|
|||
}
|
||||
|
||||
void NotificationData::setImage(const QString &imagePath) {
|
||||
if (imagePath.isEmpty() || _imageKey.empty()) {
|
||||
if (imagePath.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_notification) {
|
||||
_notification->set_icon(Gio::Icon::create(imagePath.toStdString()));
|
||||
return;
|
||||
}
|
||||
|
||||
if (_imageKey.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -696,13 +794,13 @@ bool SkipFlashBounceForCustom() {
|
|||
}
|
||||
|
||||
bool Supported() {
|
||||
return ServiceRegistered;
|
||||
return ServiceRegistered || Gio::Application::get_default();
|
||||
}
|
||||
|
||||
bool Enforced() {
|
||||
// Wayland doesn't support positioning
|
||||
// and custom notifications don't work here
|
||||
return IsWayland();
|
||||
return IsWayland() || OptionGApplication.value();
|
||||
}
|
||||
|
||||
bool ByDefault() {
|
||||
|
@ -728,25 +826,30 @@ bool ByDefault() {
|
|||
}
|
||||
|
||||
void Create(Window::Notifications::System *system) {
|
||||
static const auto ServiceWatcher = CreateServiceWatcher();
|
||||
|
||||
const auto managerSetter = [=] {
|
||||
using ManagerType = Window::Notifications::ManagerType;
|
||||
if ((Core::App().settings().nativeNotifications() || Enforced())
|
||||
&& Supported()) {
|
||||
if (system->managerType() != ManagerType::Native) {
|
||||
if (system->manager().type() != ManagerType::Native) {
|
||||
system->setManager(std::make_unique<Manager>(system));
|
||||
}
|
||||
} else if (Enforced()) {
|
||||
if (system->managerType() != ManagerType::Dummy) {
|
||||
if (system->manager().type() != ManagerType::Dummy) {
|
||||
using DummyManager = Window::Notifications::DummyManager;
|
||||
system->setManager(std::make_unique<DummyManager>(system));
|
||||
}
|
||||
} else if (system->managerType() != ManagerType::Default) {
|
||||
} else if (system->manager().type() != ManagerType::Default) {
|
||||
system->setManager(nullptr);
|
||||
}
|
||||
};
|
||||
|
||||
if (Gio::Application::get_default()) {
|
||||
managerSetter();
|
||||
return;
|
||||
}
|
||||
|
||||
static const auto ServiceWatcher = CreateServiceWatcher();
|
||||
|
||||
const auto counter = std::make_shared<int>(2);
|
||||
const auto oneReady = [=] {
|
||||
if (!--*counter) {
|
||||
|
@ -1098,7 +1201,7 @@ void Manager::doClearFromSession(not_null<Main::Session*> session) {
|
|||
}
|
||||
|
||||
bool Manager::doSkipAudio() const {
|
||||
return _private->inhibited();
|
||||
return _private->inhibited() || Gio::Application::get_default();
|
||||
}
|
||||
|
||||
bool Manager::doSkipToast() const {
|
||||
|
@ -1106,7 +1209,7 @@ bool Manager::doSkipToast() const {
|
|||
}
|
||||
|
||||
bool Manager::doSkipFlashBounce() const {
|
||||
return _private->inhibited();
|
||||
return _private->inhibited() || Gio::Application::get_default();
|
||||
}
|
||||
|
||||
} // namespace Notifications
|
||||
|
|
|
@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "platform/linux/specific_linux.h"
|
||||
|
||||
#include "base/random.h"
|
||||
#include "base/options.h"
|
||||
#include "base/platform/base_platform_info.h"
|
||||
#include "platform/linux/linux_desktop_environment.h"
|
||||
#include "platform/linux/linux_wayland_integration.h"
|
||||
|
@ -16,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "storage/localstorage.h"
|
||||
#include "core/sandbox.h"
|
||||
#include "core/application.h"
|
||||
#include "core/local_url_handlers.h"
|
||||
#include "core/core_settings.h"
|
||||
#include "core/update_checker.h"
|
||||
#include "window/window_controller.h"
|
||||
|
@ -57,6 +59,58 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
using namespace Platform;
|
||||
using Platform::internal::WaylandIntegration;
|
||||
|
||||
#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION
|
||||
typedef GApplication TDesktopApplication;
|
||||
typedef GApplicationClass TDesktopApplicationClass;
|
||||
|
||||
G_DEFINE_TYPE(
|
||||
TDesktopApplication,
|
||||
t_desktop_application,
|
||||
G_TYPE_APPLICATION)
|
||||
|
||||
static void t_desktop_application_class_init(
|
||||
TDesktopApplicationClass *klass) {
|
||||
const auto application_class = G_APPLICATION_CLASS(klass);
|
||||
|
||||
application_class->before_emit = [](
|
||||
GApplication *application,
|
||||
GVariant *platformData) {
|
||||
if (Platform::IsWayland()) {
|
||||
static const auto keys = {
|
||||
"activation-token",
|
||||
"desktop-startup-id",
|
||||
};
|
||||
for (const auto key : keys) {
|
||||
const char *token = nullptr;
|
||||
g_variant_lookup(platformData, key, "&s", &token);
|
||||
if (token) {
|
||||
qputenv("XDG_ACTIVATION_TOKEN", token);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
application_class->add_platform_data = [](
|
||||
GApplication *application,
|
||||
GVariantBuilder *builder) {
|
||||
if (Platform::IsWayland()) {
|
||||
const auto token = qgetenv("XDG_ACTIVATION_TOKEN");
|
||||
if (!token.isEmpty()) {
|
||||
g_variant_builder_add(
|
||||
builder,
|
||||
"{sv}",
|
||||
"activation-token",
|
||||
g_variant_new_string(token.constData()));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static void t_desktop_application_init(TDesktopApplication *application) {
|
||||
}
|
||||
#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION
|
||||
|
||||
namespace Platform {
|
||||
namespace {
|
||||
|
||||
|
@ -172,6 +226,197 @@ void PortalAutostart(bool start, bool silent) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LaunchGApplication() {
|
||||
const auto connection = [] {
|
||||
try {
|
||||
return Gio::DBus::Connection::get_sync(
|
||||
Gio::DBus::BusType::SESSION);
|
||||
} catch (...) {
|
||||
return Glib::RefPtr<Gio::DBus::Connection>();
|
||||
}
|
||||
}();
|
||||
|
||||
using namespace base::Platform::DBus;
|
||||
const auto activatableNames = [&] {
|
||||
try {
|
||||
if (connection) {
|
||||
return ListActivatableNames(connection);
|
||||
}
|
||||
} catch (...) {
|
||||
}
|
||||
|
||||
return std::vector<Glib::ustring>();
|
||||
}();
|
||||
|
||||
const auto freedesktopNotifications = [&] {
|
||||
try {
|
||||
if (connection && NameHasOwner(
|
||||
connection,
|
||||
"org.freedesktop.Notifications")) {
|
||||
return true;
|
||||
}
|
||||
} catch (...) {
|
||||
}
|
||||
|
||||
if (ranges::contains(
|
||||
activatableNames,
|
||||
"org.freedesktop.Notifications")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
const auto gtkNotifications = [&] {
|
||||
try {
|
||||
if (connection && NameHasOwner(
|
||||
connection,
|
||||
"org.gtk.Notifications")) {
|
||||
return true;
|
||||
}
|
||||
} catch (...) {
|
||||
}
|
||||
|
||||
if (ranges::contains(activatableNames, "org.gtk.Notifications")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
if (OptionGApplication.value()
|
||||
|| gtkNotifications()
|
||||
|| (KSandbox::isFlatpak() && !freedesktopNotifications())) {
|
||||
Glib::signal_idle().connect_once([] {
|
||||
const auto appId = QGuiApplication::desktopFileName()
|
||||
.chopped(8)
|
||||
.toStdString();
|
||||
|
||||
const auto app = Glib::wrap(
|
||||
G_APPLICATION(
|
||||
g_object_new(
|
||||
t_desktop_application_get_type(),
|
||||
"application-id",
|
||||
Gio::Application::id_is_valid(appId)
|
||||
? appId.c_str()
|
||||
: nullptr,
|
||||
"flags",
|
||||
G_APPLICATION_HANDLES_OPEN,
|
||||
nullptr)));
|
||||
|
||||
app->signal_startup().connect([=] {
|
||||
QEventLoop loop;
|
||||
loop.exec(QEventLoop::ApplicationExec);
|
||||
app->quit();
|
||||
}, true);
|
||||
|
||||
app->signal_activate().connect([] {
|
||||
Core::Sandbox::Instance().customEnterFromEventLoop([] {
|
||||
if (const auto w = App::wnd()) {
|
||||
w->activate();
|
||||
}
|
||||
});
|
||||
}, true);
|
||||
|
||||
app->signal_open().connect([](
|
||||
const Gio::Application::type_vec_files &files,
|
||||
const Glib::ustring &hint) {
|
||||
Core::Sandbox::Instance().customEnterFromEventLoop([&] {
|
||||
for (const auto file : files) {
|
||||
if (file->get_uri_scheme() == "file") {
|
||||
gSendPaths.append(
|
||||
QString::fromStdString(file->get_path()));
|
||||
continue;
|
||||
}
|
||||
const auto url = QString::fromStdString(
|
||||
file->get_uri());
|
||||
if (url.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
if (url.startsWith(qstr("interpret://"))) {
|
||||
gSendPaths.append(url);
|
||||
continue;
|
||||
}
|
||||
if (Core::StartUrlRequiresActivate(url)) {
|
||||
if (const auto w = App::wnd()) {
|
||||
w->activate();
|
||||
}
|
||||
}
|
||||
cSetStartUrl(url);
|
||||
Core::App().checkStartUrl();
|
||||
}
|
||||
if (!cSendPaths().isEmpty()) {
|
||||
if (const auto w = App::wnd()) {
|
||||
w->sendPaths();
|
||||
}
|
||||
}
|
||||
});
|
||||
}, true);
|
||||
|
||||
app->add_action("Quit", [] {
|
||||
Core::Sandbox::Instance().customEnterFromEventLoop([] {
|
||||
Core::Quit();
|
||||
});
|
||||
});
|
||||
|
||||
using Window::Notifications::Manager;
|
||||
using NotificationId = Manager::NotificationId;
|
||||
using NotificationIdTuple = std::result_of<
|
||||
decltype(&NotificationId::toTuple)(NotificationId*)
|
||||
>::type;
|
||||
|
||||
const auto notificationIdVariantType = [] {
|
||||
try {
|
||||
return base::Platform::MakeGlibVariant(
|
||||
NotificationId().toTuple()).get_type();
|
||||
} catch (...) {
|
||||
return Glib::VariantType();
|
||||
}
|
||||
}();
|
||||
|
||||
app->add_action_with_parameter(
|
||||
"notification-reply",
|
||||
notificationIdVariantType,
|
||||
[](const Glib::VariantBase ¶meter) {
|
||||
Core::Sandbox::Instance().customEnterFromEventLoop([&] {
|
||||
try {
|
||||
const auto &app = Core::App();
|
||||
const auto ¬ifications = app.notifications();
|
||||
notifications.manager().notificationActivated(
|
||||
NotificationId::FromTuple(
|
||||
base::Platform::GlibVariantCast<
|
||||
NotificationIdTuple
|
||||
>(parameter)));
|
||||
} catch (...) {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
app->add_action_with_parameter(
|
||||
"notification-mark-as-read",
|
||||
notificationIdVariantType,
|
||||
[](const Glib::VariantBase ¶meter) {
|
||||
Core::Sandbox::Instance().customEnterFromEventLoop([&] {
|
||||
try {
|
||||
const auto &app = Core::App();
|
||||
const auto ¬ifications = app.notifications();
|
||||
notifications.manager().notificationReplied(
|
||||
NotificationId::FromTuple(
|
||||
base::Platform::GlibVariantCast<
|
||||
NotificationIdTuple
|
||||
>(parameter)),
|
||||
{});
|
||||
} catch (...) {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
app->hold();
|
||||
app->run(0, nullptr);
|
||||
});
|
||||
}
|
||||
}
|
||||
#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION
|
||||
|
||||
bool GenerateDesktopFile(
|
||||
|
@ -587,6 +832,10 @@ namespace ThirdParty {
|
|||
void start() {
|
||||
LOG(("Icon theme: %1").arg(QIcon::themeName()));
|
||||
LOG(("Fallback icon theme: %1").arg(QIcon::fallbackThemeName()));
|
||||
|
||||
#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION
|
||||
LaunchGApplication();
|
||||
#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION
|
||||
}
|
||||
|
||||
void finish() {
|
||||
|
|
26
Telegram/SourceFiles/platform/platform_specific.cpp
Normal file
26
Telegram/SourceFiles/platform/platform_specific.cpp
Normal file
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "platform/platform_specific.h"
|
||||
|
||||
#include "base/options.h"
|
||||
|
||||
namespace Platform {
|
||||
|
||||
const char kOptionGApplication[] = "gapplication";
|
||||
|
||||
base::options::toggle OptionGApplication({
|
||||
.id = kOptionGApplication,
|
||||
.name = "GApplication",
|
||||
.description = "Force enable GNOME's GApplication and GNotification."
|
||||
" This changes notification behavior to be native to GNOME."
|
||||
" When disabled, autodetect is used.",
|
||||
.scope = base::options::linux,
|
||||
.restartRequired = true,
|
||||
});
|
||||
|
||||
} // namespace Platform
|
|
@ -7,8 +7,20 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
*/
|
||||
#pragma once
|
||||
|
||||
namespace base::options {
|
||||
|
||||
template <typename Type>
|
||||
class option;
|
||||
|
||||
using toggle = option<bool>;
|
||||
|
||||
} // namespace base::options
|
||||
|
||||
namespace Platform {
|
||||
|
||||
extern const char kOptionGApplication[];
|
||||
extern base::options::toggle OptionGApplication;
|
||||
|
||||
void start();
|
||||
void finish();
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "ui/chat/chat_style_radius.h"
|
||||
#include "base/options.h"
|
||||
#include "core/application.h"
|
||||
#include "platform/platform_specific.h"
|
||||
#include "chat_helpers/tabbed_panel.h"
|
||||
#include "dialogs/dialogs_inner_widget.h"
|
||||
#include "history/history_widget.h"
|
||||
|
@ -143,6 +144,7 @@ void SetupExperimental(
|
|||
addToggle(Settings::kOptionMonoSettingsIcons);
|
||||
addToggle(Webview::kOptionWebviewDebugEnabled);
|
||||
addToggle(kOptionAutoScrollInactiveChat);
|
||||
addToggle(Platform::kOptionGApplication);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
|
|
@ -125,9 +125,9 @@ void System::setManager(std::unique_ptr<Manager> manager) {
|
|||
}
|
||||
}
|
||||
|
||||
ManagerType System::managerType() const {
|
||||
Manager &System::manager() const {
|
||||
Expects(_manager != nullptr);
|
||||
return _manager->type();
|
||||
return *_manager;
|
||||
}
|
||||
|
||||
Main::Session *System::findSession(uint64 sessionId) const {
|
||||
|
|
|
@ -84,7 +84,7 @@ public:
|
|||
|
||||
void createManager();
|
||||
void setManager(std::unique_ptr<Manager> manager);
|
||||
[[nodiscard]] ManagerType managerType() const;
|
||||
[[nodiscard]] Manager &manager() const;
|
||||
|
||||
void checkDelayed();
|
||||
void schedule(Data::ItemNotification notification);
|
||||
|
@ -217,6 +217,22 @@ public:
|
|||
friend inline auto operator<=>(
|
||||
const ContextId&,
|
||||
const ContextId&) = default;
|
||||
|
||||
[[nodiscard]] auto toTuple() const {
|
||||
return std::make_tuple(
|
||||
sessionId,
|
||||
peerId.value,
|
||||
topicRootId.bare);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]] static auto FromTuple(const T &tuple) {
|
||||
return ContextId{
|
||||
std::get<0>(tuple),
|
||||
PeerIdHelper(std::get<1>(tuple)),
|
||||
std::get<2>(tuple),
|
||||
};
|
||||
}
|
||||
};
|
||||
struct NotificationId {
|
||||
ContextId contextId;
|
||||
|
@ -225,6 +241,20 @@ public:
|
|||
friend inline auto operator<=>(
|
||||
const NotificationId&,
|
||||
const NotificationId&) = default;
|
||||
|
||||
[[nodiscard]] auto toTuple() const {
|
||||
return std::make_tuple(
|
||||
contextId.toTuple(),
|
||||
msgId.bare);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]] static auto FromTuple(const T &tuple) {
|
||||
return NotificationId{
|
||||
ContextId::FromTuple(std::get<0>(tuple)),
|
||||
std::get<1>(tuple),
|
||||
};
|
||||
}
|
||||
};
|
||||
struct NotificationFields {
|
||||
not_null<HistoryItem*> item;
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 1cc6aba584869df04341467cf7c5f72c8354086f
|
||||
Subproject commit d3f9e826197c6ed42f8ef2d021d8eb74b2d390cc
|
|
@ -1 +1 @@
|
|||
Subproject commit b9d81771a0d7533dd07805f0618193277715da80
|
||||
Subproject commit 9623bb460b9be6b0481d775f9e01e5084170024e
|
Loading…
Add table
Reference in a new issue