Move notification closing to NotificationData destruction

This commit is contained in:
Ilya Fedin 2025-03-03 18:12:36 +00:00 committed by John Preston
parent b962309498
commit 345b2cb835

View file

@ -158,25 +158,21 @@ public:
void clearNotification(NotificationId id); void clearNotification(NotificationId id);
void invokeIfNotInhibited(Fn<void()> callback); void invokeIfNotInhibited(Fn<void()> callback);
~Private();
private: private:
struct NotificationData : public base::has_weak_ptr { struct NotificationData : public base::has_weak_ptr {
uint id = 0; std::variant<v::null_t, uint, std::string> id;
rpl::lifetime lifetime;
}; };
using Notification = std::unique_ptr<NotificationData>; using Notification = std::unique_ptr<NotificationData>;
const not_null<Manager*> _manager; const not_null<Manager*> _manager;
base::flat_map<
ContextId,
base::flat_map<MsgId,
std::variant<Notification, std::string>>> _notifications;
Gio::Application _application; Gio::Application _application;
XdgNotifications::NotificationsProxy _proxy; XdgNotifications::NotificationsProxy _proxy;
XdgNotifications::Notifications _interface; XdgNotifications::Notifications _interface;
Media::Audio::LocalDiskCache _sounds; Media::Audio::LocalDiskCache _sounds;
base::flat_map<
ContextId,
base::flat_map<MsgId, Notification>> _notifications;
rpl::lifetime _lifetime; rpl::lifetime _lifetime;
}; };
@ -427,7 +423,7 @@ void Manager::Private::init(XdgNotifications::NotificationsProxy proxy) {
Core::Sandbox::Instance().customEnterFromEventLoop([&] { Core::Sandbox::Instance().customEnterFromEventLoop([&] {
for (const auto &[key, notifications] : _notifications) { for (const auto &[key, notifications] : _notifications) {
for (const auto &[msgId, notification] : notifications) { for (const auto &[msgId, notification] : notifications) {
if (id == v::get<Notification>(notification)->id) { if (id == v::get<uint>(notification->id)) {
if (actionName == "default") { if (actionName == "default") {
_manager->notificationActivated({ key, msgId }); _manager->notificationActivated({ key, msgId });
} else if (actionName == "mail-mark-read") { } else if (actionName == "mail-mark-read") {
@ -451,7 +447,7 @@ void Manager::Private::init(XdgNotifications::NotificationsProxy proxy) {
Core::Sandbox::Instance().customEnterFromEventLoop([&] { Core::Sandbox::Instance().customEnterFromEventLoop([&] {
for (const auto &[key, notifications] : _notifications) { for (const auto &[key, notifications] : _notifications) {
for (const auto &[msgId, notification] : notifications) { for (const auto &[msgId, notification] : notifications) {
if (id == v::get<Notification>(notification)->id) { if (id == v::get<uint>(notification->id)) {
_manager->notificationReplied( _manager->notificationReplied(
{ key, msgId }, { key, msgId },
{ QString::fromStdString(text), {} }); { QString::fromStdString(text), {} });
@ -472,7 +468,7 @@ void Manager::Private::init(XdgNotifications::NotificationsProxy proxy) {
std::string token) { std::string token) {
for (const auto &[key, notifications] : _notifications) { for (const auto &[key, notifications] : _notifications) {
for (const auto &[msgId, notification] : notifications) { for (const auto &[msgId, notification] : notifications) {
if (id == v::get<Notification>(notification)->id) { if (id == v::get<uint>(notification->id)) {
GLib::setenv("XDG_ACTIVATION_TOKEN", token, true); GLib::setenv("XDG_ACTIVATION_TOKEN", token, true);
return; return;
} }
@ -505,8 +501,7 @@ void Manager::Private::init(XdgNotifications::NotificationsProxy proxy) {
* In all other cases we keep the notification reference so that we may clear the notification later from history, * 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 the message for that notification is read (e.g. chat is opened or read from another device).
*/ */
if (id == v::get<Notification>(notification)->id if (id == v::get<uint>(notification->id) && reason == 2) {
&& reason == 2) {
clearNotification({ key, msgId }); clearNotification({ key, msgId });
return; return;
} }
@ -535,18 +530,16 @@ void Manager::Private::showNotification(
.msgId = info.itemId, .msgId = info.itemId,
}; };
auto notification = _application auto notification = _application
? std::variant<Notification, Gio::Notification>( ? Gio::Notification::new_(
Gio::Notification::new_( info.subtitle.isEmpty()
info.subtitle.isEmpty() ? info.title.toStdString()
? info.title.toStdString() : info.subtitle.toStdString()
: info.subtitle.toStdString() + " (" + info.title.toStdString() + ')')
+ " (" + info.title.toStdString() + ')')) : Gio::Notification();
: std::variant<Notification, Gio::Notification>(
std::make_unique<NotificationData>());
std::vector<gi::cstring> actions; std::vector<gi::cstring> actions;
auto hints = GLib::VariantDict::new_(); auto hints = GLib::VariantDict::new_();
v::match(notification, [&](Gio::Notification &notification) { if (notification) {
notification.set_body(info.message.toStdString()); notification.set_body(info.message.toStdString());
notification.set_icon( notification.set_icon(
@ -601,7 +594,7 @@ void Manager::Private::showNotification(
"app.notification-mark-as-read", "app.notification-mark-as-read",
notificationVariant); notificationVariant);
} }
}, [&](const Notification &notification) { } else {
if (HasCapability("actions")) { if (HasCapability("actions")) {
actions.push_back("default"); actions.push_back("default");
actions.push_back(tr::lng_open_link(tr::now).toStdString()); actions.push_back(tr::lng_open_link(tr::now).toStdString());
@ -661,11 +654,11 @@ void Manager::Private::showNotification(
hints.insert_value("desktop-entry", GLib::Variant::new_string( hints.insert_value("desktop-entry", GLib::Variant::new_string(
QGuiApplication::desktopFileName().toStdString())); QGuiApplication::desktopFileName().toStdString()));
}); }
const auto imageKey = GetImageKey(); const auto imageKey = GetImageKey();
if (!options.hideNameAndPhoto) { if (!options.hideNameAndPhoto) {
v::match(notification, [&](Gio::Notification &notification) { if (notification) {
QByteArray imageData; QByteArray imageData;
QBuffer buffer(&imageData); QBuffer buffer(&imageData);
buffer.open(QIODevice::WriteOnly); buffer.open(QIODevice::WriteOnly);
@ -679,11 +672,7 @@ void Manager::Private::showNotification(
reinterpret_cast<const uchar*>(imageData.constData()), reinterpret_cast<const uchar*>(imageData.constData()),
imageData.size(), imageData.size(),
[imageData] {}))); [imageData] {})));
}, [&](const Notification &notification) { } else if (!imageKey.empty()) {
if (imageKey.empty()) {
return;
}
const auto image = Window::Notifications::GenerateUserpic( const auto image = Window::Notifications::GenerateUserpic(
peer, peer,
userpicView userpicView
@ -703,43 +692,27 @@ void Manager::Private::showNotification(
true, true,
[image] {}), [image] {}),
})); }));
});
}
auto i = _notifications.find(key);
if (i != end(_notifications)) {
auto j = i->second.find(info.itemId);
if (j != end(i->second)) {
auto oldNotification = std::move(j->second);
i->second.erase(j);
v::match(oldNotification, [&](
const std::string &oldNotification) {
_application.withdraw_notification(oldNotification);
}, [&](const Notification &oldNotification) {
_interface.call_close_notification(
oldNotification->id,
nullptr);
});
} }
} else {
i = _notifications.emplace(key).first;
} }
v::match(notification, [&](Gio::Notification &notification) {
const auto j = i->second.emplace(
info.itemId,
Gio::dbus_generate_guid()).first;
_application.send_notification(
v::get<std::string>(j->second),
notification);
}, [&](Notification &notification) {
const auto j = i->second.emplace(
info.itemId,
std::move(notification)).first;
const auto weak = base::make_weak( const auto &data
v::get<Notification>(j->second).get()); = _notifications[key][info.itemId]
= std::make_unique<NotificationData>();
data->lifetime.add([=, notification = data.get()] {
v::match(notification->id, [&](const std::string &id) {
_application.withdraw_notification(id);
}, [&](uint id) {
_interface.call_close_notification(id, nullptr);
}, [](v::null_t) {});
});
if (notification) {
const auto id = Gio::dbus_generate_guid();
data->id = id;
_application.send_notification(id, notification);
} else {
// work around snap's activation restriction // work around snap's activation restriction
const auto weak = base::make_weak(data);
StartServiceAsync( StartServiceAsync(
_proxy.get_connection(), _proxy.get_connection(),
crl::guard(weak, [=]() mutable { crl::guard(weak, [=]() mutable {
@ -808,19 +781,11 @@ void Manager::Private::showNotification(
&callbackWrap->wrapper, &callbackWrap->wrapper,
callbackWrap); callbackWrap);
})); }));
}); }
} }
void Manager::Private::clearAll() { void Manager::Private::clearAll() {
for (const auto &[key, notifications] : base::take(_notifications)) { _notifications.clear();
for (const auto &[msgId, notification] : notifications) {
v::match(notification, [&](const std::string &notification) {
_application.withdraw_notification(notification);
}, [&](const Notification &notification) {
_interface.call_close_notification(notification->id, nullptr);
});
}
}
} }
void Manager::Private::clearFromItem(not_null<HistoryItem*> item) { void Manager::Private::clearFromItem(not_null<HistoryItem*> item) {
@ -837,16 +802,10 @@ void Manager::Private::clearFromItem(not_null<HistoryItem*> item) {
if (j == i->second.end()) { if (j == i->second.end()) {
return; return;
} }
const auto taken = base::take(j->second);
i->second.erase(j); i->second.erase(j);
if (i->second.empty()) { if (i->second.empty()) {
_notifications.erase(i); _notifications.erase(i);
} }
v::match(taken, [&](const std::string &taken) {
_application.withdraw_notification(taken);
}, [&](const Notification &taken) {
_interface.call_close_notification(taken->id, nullptr);
});
} }
void Manager::Private::clearFromTopic(not_null<Data::ForumTopic*> topic) { void Manager::Private::clearFromTopic(not_null<Data::ForumTopic*> topic) {
@ -856,16 +815,7 @@ void Manager::Private::clearFromTopic(not_null<Data::ForumTopic*> topic) {
}; };
const auto i = _notifications.find(key); const auto i = _notifications.find(key);
if (i != _notifications.cend()) { if (i != _notifications.cend()) {
const auto temp = base::take(i->second);
_notifications.erase(i); _notifications.erase(i);
for (const auto &[msgId, notification] : temp) {
v::match(notification, [&](const std::string &notification) {
_application.withdraw_notification(notification);
}, [&](const Notification &notification) {
_interface.call_close_notification(notification->id, nullptr);
});
}
} }
} }
@ -879,16 +829,7 @@ void Manager::Private::clearFromHistory(not_null<History*> history) {
while (i != _notifications.cend() while (i != _notifications.cend()
&& i->first.sessionId == sessionId && i->first.sessionId == sessionId
&& i->first.peerId == peerId) { && i->first.peerId == peerId) {
const auto temp = base::take(i->second);
i = _notifications.erase(i); i = _notifications.erase(i);
for (const auto &[msgId, notification] : temp) {
v::match(notification, [&](const std::string &notification) {
_application.withdraw_notification(notification);
}, [&](const Notification &notification) {
_interface.call_close_notification(notification->id, nullptr);
});
}
} }
} }
@ -898,16 +839,7 @@ void Manager::Private::clearFromSession(not_null<Main::Session*> session) {
.sessionId = sessionId, .sessionId = sessionId,
}); });
while (i != _notifications.cend() && i->first.sessionId == sessionId) { while (i != _notifications.cend() && i->first.sessionId == sessionId) {
const auto temp = base::take(i->second);
i = _notifications.erase(i); i = _notifications.erase(i);
for (const auto &[msgId, notification] : temp) {
v::match(notification, [&](const std::string &notification) {
_application.withdraw_notification(notification);
}, [&](const Notification &notification) {
_interface.call_close_notification(notification->id, nullptr);
});
}
} }
} }
@ -926,10 +858,6 @@ void Manager::Private::invokeIfNotInhibited(Fn<void()> callback) {
} }
} }
Manager::Private::~Private() {
clearAll();
}
Manager::Manager(not_null<Window::Notifications::System*> system) Manager::Manager(not_null<Window::Notifications::System*> system)
: NativeManager(system) : NativeManager(system)
, _private(std::make_unique<Private>(this)) { , _private(std::make_unique<Private>(this)) {