mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Turn NotificationData into a struct
This commit is contained in:
parent
b07d3c5403
commit
a8d1eadfbf
2 changed files with 279 additions and 332 deletions
|
@ -139,291 +139,6 @@ bool UseGNotification() {
|
||||||
return KSandbox::isFlatpak() && !ServiceRegistered;
|
return KSandbox::isFlatpak() && !ServiceRegistered;
|
||||||
}
|
}
|
||||||
|
|
||||||
class NotificationData final : public base::has_weak_ptr {
|
|
||||||
public:
|
|
||||||
using NotificationId = Window::Notifications::Manager::NotificationId;
|
|
||||||
using Info = Window::Notifications::NativeManager::NotificationInfo;
|
|
||||||
|
|
||||||
NotificationData(
|
|
||||||
not_null<Manager*> manager,
|
|
||||||
XdgNotifications::NotificationsProxy proxy,
|
|
||||||
NotificationId id,
|
|
||||||
const Info &info);
|
|
||||||
|
|
||||||
NotificationData(const NotificationData &other) = delete;
|
|
||||||
NotificationData &operator=(const NotificationData &other) = delete;
|
|
||||||
NotificationData(NotificationData &&other) = delete;
|
|
||||||
NotificationData &operator=(NotificationData &&other) = delete;
|
|
||||||
|
|
||||||
void show();
|
|
||||||
void close();
|
|
||||||
void setImage(QImage image);
|
|
||||||
|
|
||||||
private:
|
|
||||||
const not_null<Manager*> _manager;
|
|
||||||
NotificationId _id;
|
|
||||||
|
|
||||||
Media::Audio::LocalDiskCache _sounds;
|
|
||||||
|
|
||||||
XdgNotifications::NotificationsProxy _proxy;
|
|
||||||
XdgNotifications::Notifications _interface;
|
|
||||||
std::string _title;
|
|
||||||
std::string _body;
|
|
||||||
std::vector<gi::cstring> _actions;
|
|
||||||
GLib::VariantDict _hints;
|
|
||||||
std::string _imageKey;
|
|
||||||
|
|
||||||
uint _notificationId = 0;
|
|
||||||
rpl::lifetime _lifetime;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
using Notification = std::unique_ptr<NotificationData>;
|
|
||||||
|
|
||||||
NotificationData::NotificationData(
|
|
||||||
not_null<Manager*> manager,
|
|
||||||
XdgNotifications::NotificationsProxy proxy,
|
|
||||||
NotificationId id,
|
|
||||||
const Info &info)
|
|
||||||
: _manager(manager)
|
|
||||||
, _id(id)
|
|
||||||
, _sounds(cWorkingDir() + u"tdata/audio_cache"_q)
|
|
||||||
, _proxy(proxy)
|
|
||||||
, _interface(proxy)
|
|
||||||
, _hints(GLib::VariantDict::new_())
|
|
||||||
, _imageKey(GetImageKey()) {
|
|
||||||
const auto &title = info.title;
|
|
||||||
const auto &subtitle = info.subtitle;
|
|
||||||
const auto &text = info.message;
|
|
||||||
if (HasCapability("body-markup")) {
|
|
||||||
_title = title.toStdString();
|
|
||||||
|
|
||||||
_body = subtitle.isEmpty()
|
|
||||||
? text.toHtmlEscaped().toStdString()
|
|
||||||
: u"<b>%1</b>\n%2"_q.arg(
|
|
||||||
subtitle.toHtmlEscaped(),
|
|
||||||
text.toHtmlEscaped()).toStdString();
|
|
||||||
} else {
|
|
||||||
_title = subtitle.isEmpty()
|
|
||||||
? title.toStdString()
|
|
||||||
: subtitle.toStdString() + " (" + title.toStdString() + ')';
|
|
||||||
|
|
||||||
_body = text.toStdString();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (HasCapability("actions")) {
|
|
||||||
_actions.push_back("default");
|
|
||||||
_actions.push_back(tr::lng_open_link(tr::now).toStdString());
|
|
||||||
|
|
||||||
if (!info.options.hideMarkAsRead) {
|
|
||||||
// icon name according to https://specifications.freedesktop.org/icon-naming-spec/icon-naming-spec-latest.html
|
|
||||||
_actions.push_back("mail-mark-read");
|
|
||||||
_actions.push_back(
|
|
||||||
tr::lng_context_mark_read(tr::now).toStdString());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (HasCapability("inline-reply")
|
|
||||||
&& !info.options.hideReplyButton) {
|
|
||||||
_actions.push_back("inline-reply");
|
|
||||||
_actions.push_back(
|
|
||||||
tr::lng_notification_reply(tr::now).toStdString());
|
|
||||||
|
|
||||||
const auto notificationRepliedSignalId
|
|
||||||
= _interface.signal_notification_replied().connect([=](
|
|
||||||
XdgNotifications::Notifications,
|
|
||||||
uint id,
|
|
||||||
std::string text) {
|
|
||||||
Core::Sandbox::Instance().customEnterFromEventLoop([&] {
|
|
||||||
if (id == _notificationId) {
|
|
||||||
_manager->notificationReplied(
|
|
||||||
_id,
|
|
||||||
{ QString::fromStdString(text), {} });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
_lifetime.add([=] {
|
|
||||||
_interface.disconnect(notificationRepliedSignalId);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto actionInvokedSignalId
|
|
||||||
= _interface.signal_action_invoked().connect([=](
|
|
||||||
XdgNotifications::Notifications,
|
|
||||||
uint id,
|
|
||||||
std::string actionName) {
|
|
||||||
Core::Sandbox::Instance().customEnterFromEventLoop([&] {
|
|
||||||
if (id == _notificationId) {
|
|
||||||
if (actionName == "default") {
|
|
||||||
_manager->notificationActivated(_id);
|
|
||||||
} else if (actionName == "mail-mark-read") {
|
|
||||||
_manager->notificationReplied(_id, {});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
_lifetime.add([=] {
|
|
||||||
_interface.disconnect(actionInvokedSignalId);
|
|
||||||
});
|
|
||||||
|
|
||||||
const auto activationTokenSignalId
|
|
||||||
= _interface.signal_activation_token().connect([=](
|
|
||||||
XdgNotifications::Notifications,
|
|
||||||
uint id,
|
|
||||||
std::string token) {
|
|
||||||
if (id == _notificationId) {
|
|
||||||
GLib::setenv("XDG_ACTIVATION_TOKEN", token, true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
_lifetime.add([=] {
|
|
||||||
_interface.disconnect(activationTokenSignalId);
|
|
||||||
});
|
|
||||||
|
|
||||||
_actions.push_back({});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (HasCapability("action-icons")) {
|
|
||||||
_hints.insert_value("action-icons", GLib::Variant::new_boolean(true));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (HasCapability("sound")) {
|
|
||||||
const auto sound = info.sound
|
|
||||||
? info.sound()
|
|
||||||
: Media::Audio::LocalSound();
|
|
||||||
|
|
||||||
const auto path = sound
|
|
||||||
? _sounds.path(sound).toStdString()
|
|
||||||
: std::string();
|
|
||||||
|
|
||||||
if (!path.empty()) {
|
|
||||||
_hints.insert_value(
|
|
||||||
"sound-file",
|
|
||||||
GLib::Variant::new_string(path));
|
|
||||||
} else {
|
|
||||||
_hints.insert_value(
|
|
||||||
"suppress-sound",
|
|
||||||
GLib::Variant::new_boolean(true));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (HasCapability("x-canonical-append")) {
|
|
||||||
_hints.insert_value(
|
|
||||||
"x-canonical-append",
|
|
||||||
GLib::Variant::new_string("true"));
|
|
||||||
}
|
|
||||||
|
|
||||||
_hints.insert_value("category", GLib::Variant::new_string("im.received"));
|
|
||||||
|
|
||||||
_hints.insert_value("desktop-entry", GLib::Variant::new_string(
|
|
||||||
QGuiApplication::desktopFileName().toStdString()));
|
|
||||||
|
|
||||||
const auto notificationClosedSignalId =
|
|
||||||
_interface.signal_notification_closed().connect([=](
|
|
||||||
XdgNotifications::Notifications,
|
|
||||||
uint id,
|
|
||||||
uint reason) {
|
|
||||||
Core::Sandbox::Instance().customEnterFromEventLoop([&] {
|
|
||||||
/*
|
|
||||||
* 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) {
|
|
||||||
_manager->clearNotification(_id);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
_lifetime.add([=] {
|
|
||||||
_interface.disconnect(notificationClosedSignalId);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void NotificationData::show() {
|
|
||||||
// a hack for snap's activation restriction
|
|
||||||
const auto weak = base::make_weak(this);
|
|
||||||
StartServiceAsync(_proxy.get_connection(), crl::guard(weak, [=] {
|
|
||||||
const auto callbackWrap = gi::unwrap(
|
|
||||||
Gio::AsyncReadyCallback(
|
|
||||||
crl::guard(weak, [=](GObject::Object, Gio::AsyncResult res) {
|
|
||||||
Core::Sandbox::Instance().customEnterFromEventLoop([&] {
|
|
||||||
const auto result = _interface.call_notify_finish(
|
|
||||||
res);
|
|
||||||
|
|
||||||
if (!result) {
|
|
||||||
Gio::DBusErrorNS_::strip_remote_error(
|
|
||||||
result.error());
|
|
||||||
LOG(("Native Notification Error: %1").arg(
|
|
||||||
result.error().message_().c_str()));
|
|
||||||
_manager->clearNotification(_id);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_notificationId = std::get<1>(*result);
|
|
||||||
});
|
|
||||||
})),
|
|
||||||
gi::scope_async);
|
|
||||||
|
|
||||||
xdg_notifications_notifications_call_notify(
|
|
||||||
_interface.gobj_(),
|
|
||||||
AppName.data(),
|
|
||||||
0,
|
|
||||||
(_imageKey.empty() || !_hints.lookup_value(_imageKey)
|
|
||||||
? base::IconName().toStdString()
|
|
||||||
: std::string()).c_str(),
|
|
||||||
_title.c_str(),
|
|
||||||
_body.c_str(),
|
|
||||||
!_actions.empty()
|
|
||||||
? (_actions
|
|
||||||
| ranges::views::transform(&gi::cstring::c_str)
|
|
||||||
| ranges::to_vector).data()
|
|
||||||
: nullptr,
|
|
||||||
_hints.end().gobj_(),
|
|
||||||
-1,
|
|
||||||
nullptr,
|
|
||||||
&callbackWrap->wrapper,
|
|
||||||
callbackWrap);
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
void NotificationData::close() {
|
|
||||||
_interface.call_close_notification(_notificationId, nullptr);
|
|
||||||
_manager->clearNotification(_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
void NotificationData::setImage(QImage image) {
|
|
||||||
if (_imageKey.empty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
image.convertTo(QImage::Format_RGBA8888);
|
|
||||||
_hints.insert_value(_imageKey, GLib::Variant::new_tuple({
|
|
||||||
GLib::Variant::new_int32(image.width()),
|
|
||||||
GLib::Variant::new_int32(image.height()),
|
|
||||||
GLib::Variant::new_int32(image.bytesPerLine()),
|
|
||||||
GLib::Variant::new_boolean(true),
|
|
||||||
GLib::Variant::new_int32(8),
|
|
||||||
GLib::Variant::new_int32(4),
|
|
||||||
GLib::Variant::new_from_data(
|
|
||||||
GLib::VariantType::new_("ay"),
|
|
||||||
reinterpret_cast<const uchar*>(image.constBits()),
|
|
||||||
image.sizeInBytes(),
|
|
||||||
true,
|
|
||||||
[image] {}),
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
class Manager::Private {
|
class Manager::Private {
|
||||||
|
@ -446,6 +161,12 @@ public:
|
||||||
~Private();
|
~Private();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
struct NotificationData : public base::has_weak_ptr {
|
||||||
|
uint id = 0;
|
||||||
|
rpl::lifetime lifetime;
|
||||||
|
};
|
||||||
|
using Notification = std::unique_ptr<NotificationData>;
|
||||||
|
|
||||||
const not_null<Manager*> _manager;
|
const not_null<Manager*> _manager;
|
||||||
|
|
||||||
base::flat_map<
|
base::flat_map<
|
||||||
|
@ -456,6 +177,7 @@ private:
|
||||||
Gio::Application _application;
|
Gio::Application _application;
|
||||||
XdgNotifications::NotificationsProxy _proxy;
|
XdgNotifications::NotificationsProxy _proxy;
|
||||||
XdgNotifications::Notifications _interface;
|
XdgNotifications::Notifications _interface;
|
||||||
|
Media::Audio::LocalDiskCache _sounds;
|
||||||
rpl::lifetime _lifetime;
|
rpl::lifetime _lifetime;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -606,7 +328,8 @@ Manager::Private::Private(not_null<Manager*> manager)
|
||||||
: _manager(manager)
|
: _manager(manager)
|
||||||
, _application(UseGNotification()
|
, _application(UseGNotification()
|
||||||
? Gio::Application::get_default()
|
? Gio::Application::get_default()
|
||||||
: nullptr) {
|
: nullptr)
|
||||||
|
, _sounds(cWorkingDir() + u"tdata/audio_cache"_q) {
|
||||||
const auto &serverInformation = CurrentServerInformation;
|
const auto &serverInformation = CurrentServerInformation;
|
||||||
|
|
||||||
if (!serverInformation.name.empty()) {
|
if (!serverInformation.name.empty()) {
|
||||||
|
@ -716,16 +439,11 @@ void Manager::Private::showNotification(
|
||||||
? info.title.toStdString()
|
? info.title.toStdString()
|
||||||
: info.subtitle.toStdString()
|
: info.subtitle.toStdString()
|
||||||
+ " (" + info.title.toStdString() + ')'))
|
+ " (" + info.title.toStdString() + ')'))
|
||||||
: std::variant<Notification, Gio::Notification>(
|
: std::variant<Notification, Gio::Notification>(Notification{});
|
||||||
std::make_unique<NotificationData>(
|
|
||||||
_manager,
|
|
||||||
_proxy,
|
|
||||||
notificationId,
|
|
||||||
info));
|
|
||||||
|
|
||||||
if (const auto ptr = std::get_if<Gio::Notification>(¬ification)) {
|
|
||||||
auto ¬ification = *ptr;
|
|
||||||
|
|
||||||
|
std::vector<gi::cstring> actions;
|
||||||
|
auto hints = GLib::VariantDict::new_();
|
||||||
|
v::match(notification, [&](Gio::Notification ¬ification) {
|
||||||
notification.set_body(info.message.toStdString());
|
notification.set_body(info.message.toStdString());
|
||||||
|
|
||||||
notification.set_icon(
|
notification.set_icon(
|
||||||
|
@ -780,8 +498,157 @@ void Manager::Private::showNotification(
|
||||||
"app.notification-mark-as-read",
|
"app.notification-mark-as-read",
|
||||||
notificationVariant);
|
notificationVariant);
|
||||||
}
|
}
|
||||||
}
|
}, [&](const Notification &owned) {
|
||||||
|
const auto notification = owned.get();
|
||||||
|
|
||||||
|
if (HasCapability("actions")) {
|
||||||
|
actions.push_back("default");
|
||||||
|
actions.push_back(tr::lng_open_link(tr::now).toStdString());
|
||||||
|
|
||||||
|
if (!options.hideMarkAsRead) {
|
||||||
|
// icon name according to https://specifications.freedesktop.org/icon-naming-spec/icon-naming-spec-latest.html
|
||||||
|
actions.push_back("mail-mark-read");
|
||||||
|
actions.push_back(
|
||||||
|
tr::lng_context_mark_read(tr::now).toStdString());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (HasCapability("inline-reply")
|
||||||
|
&& !options.hideReplyButton) {
|
||||||
|
actions.push_back("inline-reply");
|
||||||
|
actions.push_back(
|
||||||
|
tr::lng_notification_reply(tr::now).toStdString());
|
||||||
|
|
||||||
|
const auto notificationRepliedSignalId
|
||||||
|
= _interface.signal_notification_replied().connect([=](
|
||||||
|
XdgNotifications::Notifications,
|
||||||
|
uint id,
|
||||||
|
std::string text) {
|
||||||
|
Core::Sandbox::Instance().customEnterFromEventLoop(
|
||||||
|
[&] {
|
||||||
|
if (id == notification->id) {
|
||||||
|
_manager->notificationReplied(
|
||||||
|
notificationId,
|
||||||
|
{ QString::fromStdString(text), {} });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
notification->lifetime.add([=] {
|
||||||
|
_interface.disconnect(notificationRepliedSignalId);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto actionInvokedSignalId
|
||||||
|
= _interface.signal_action_invoked().connect([=](
|
||||||
|
XdgNotifications::Notifications,
|
||||||
|
uint id,
|
||||||
|
std::string actionName) {
|
||||||
|
Core::Sandbox::Instance().customEnterFromEventLoop([&] {
|
||||||
|
if (id == notification->id) {
|
||||||
|
if (actionName == "default") {
|
||||||
|
_manager->notificationActivated(
|
||||||
|
notificationId);
|
||||||
|
} else if (actionName == "mail-mark-read") {
|
||||||
|
_manager->notificationReplied(
|
||||||
|
notificationId,
|
||||||
|
{});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
notification->lifetime.add([=] {
|
||||||
|
_interface.disconnect(actionInvokedSignalId);
|
||||||
|
});
|
||||||
|
|
||||||
|
const auto activationTokenSignalId
|
||||||
|
= _interface.signal_activation_token().connect([=](
|
||||||
|
XdgNotifications::Notifications,
|
||||||
|
uint id,
|
||||||
|
std::string token) {
|
||||||
|
if (id == notification->id) {
|
||||||
|
GLib::setenv("XDG_ACTIVATION_TOKEN", token, true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
notification->lifetime.add([=] {
|
||||||
|
_interface.disconnect(activationTokenSignalId);
|
||||||
|
});
|
||||||
|
|
||||||
|
actions.push_back({});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (HasCapability("action-icons")) {
|
||||||
|
hints.insert_value(
|
||||||
|
"action-icons",
|
||||||
|
GLib::Variant::new_boolean(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (HasCapability("sound")) {
|
||||||
|
const auto sound = info.sound
|
||||||
|
? info.sound()
|
||||||
|
: Media::Audio::LocalSound();
|
||||||
|
|
||||||
|
const auto path = sound
|
||||||
|
? _sounds.path(sound).toStdString()
|
||||||
|
: std::string();
|
||||||
|
|
||||||
|
if (!path.empty()) {
|
||||||
|
hints.insert_value(
|
||||||
|
"sound-file",
|
||||||
|
GLib::Variant::new_string(path));
|
||||||
|
} else {
|
||||||
|
hints.insert_value(
|
||||||
|
"suppress-sound",
|
||||||
|
GLib::Variant::new_boolean(true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (HasCapability("x-canonical-append")) {
|
||||||
|
hints.insert_value(
|
||||||
|
"x-canonical-append",
|
||||||
|
GLib::Variant::new_string("true"));
|
||||||
|
}
|
||||||
|
|
||||||
|
hints.insert_value(
|
||||||
|
"category",
|
||||||
|
GLib::Variant::new_string("im.received"));
|
||||||
|
|
||||||
|
hints.insert_value("desktop-entry", GLib::Variant::new_string(
|
||||||
|
QGuiApplication::desktopFileName().toStdString()));
|
||||||
|
|
||||||
|
const auto notificationClosedSignalId =
|
||||||
|
_interface.signal_notification_closed().connect([=](
|
||||||
|
XdgNotifications::Notifications,
|
||||||
|
uint id,
|
||||||
|
uint reason) {
|
||||||
|
Core::Sandbox::Instance().customEnterFromEventLoop([&] {
|
||||||
|
/*
|
||||||
|
* 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 == notification->id && reason == 2) {
|
||||||
|
clearNotification(notificationId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
notification->lifetime.add([=] {
|
||||||
|
_interface.disconnect(notificationClosedSignalId);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const auto imageKey = GetImageKey();
|
||||||
if (!options.hideNameAndPhoto) {
|
if (!options.hideNameAndPhoto) {
|
||||||
v::match(notification, [&](Gio::Notification ¬ification) {
|
v::match(notification, [&](Gio::Notification ¬ification) {
|
||||||
QByteArray imageData;
|
QByteArray imageData;
|
||||||
|
@ -798,8 +665,29 @@ void Manager::Private::showNotification(
|
||||||
imageData.size(),
|
imageData.size(),
|
||||||
[imageData] {})));
|
[imageData] {})));
|
||||||
}, [&](const Notification ¬ification) {
|
}, [&](const Notification ¬ification) {
|
||||||
notification->setImage(
|
if (imageKey.empty()) {
|
||||||
Window::Notifications::GenerateUserpic(peer, userpicView));
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto image = Window::Notifications::GenerateUserpic(
|
||||||
|
peer,
|
||||||
|
userpicView
|
||||||
|
).convertToFormat(QImage::Format_RGBA8888);
|
||||||
|
|
||||||
|
hints.insert_value(imageKey, GLib::Variant::new_tuple({
|
||||||
|
GLib::Variant::new_int32(image.width()),
|
||||||
|
GLib::Variant::new_int32(image.height()),
|
||||||
|
GLib::Variant::new_int32(image.bytesPerLine()),
|
||||||
|
GLib::Variant::new_boolean(true),
|
||||||
|
GLib::Variant::new_int32(8),
|
||||||
|
GLib::Variant::new_int32(4),
|
||||||
|
GLib::Variant::new_from_data(
|
||||||
|
GLib::VariantType::new_("ay"),
|
||||||
|
reinterpret_cast<const uchar*>(image.constBits()),
|
||||||
|
image.sizeInBytes(),
|
||||||
|
true,
|
||||||
|
[image] {}),
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -812,10 +700,12 @@ void Manager::Private::showNotification(
|
||||||
v::match(oldNotification, [&](
|
v::match(oldNotification, [&](
|
||||||
const std::string &oldNotification) {
|
const std::string &oldNotification) {
|
||||||
_application.withdraw_notification(oldNotification);
|
_application.withdraw_notification(oldNotification);
|
||||||
clearNotification(notificationId);
|
|
||||||
}, [&](const Notification &oldNotification) {
|
}, [&](const Notification &oldNotification) {
|
||||||
oldNotification->close();
|
_interface.call_close_notification(
|
||||||
|
oldNotification->id,
|
||||||
|
nullptr);
|
||||||
});
|
});
|
||||||
|
clearNotification(notificationId);
|
||||||
i = _notifications.find(key);
|
i = _notifications.find(key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -833,23 +723,85 @@ void Manager::Private::showNotification(
|
||||||
const auto j = i->second.emplace(
|
const auto j = i->second.emplace(
|
||||||
info.itemId,
|
info.itemId,
|
||||||
std::move(notification)).first;
|
std::move(notification)).first;
|
||||||
v::get<Notification>(j->second)->show();
|
|
||||||
|
const auto weak = base::make_weak(
|
||||||
|
v::get<Notification>(j->second).get());
|
||||||
|
|
||||||
|
// work around snap's activation restriction
|
||||||
|
StartServiceAsync(
|
||||||
|
_proxy.get_connection(),
|
||||||
|
crl::guard(weak, [=]() mutable {
|
||||||
|
const auto hasBodyMarkup = HasCapability("body-markup");
|
||||||
|
|
||||||
|
const auto callbackWrap = gi::unwrap(
|
||||||
|
Gio::AsyncReadyCallback(
|
||||||
|
crl::guard(weak, [=](
|
||||||
|
GObject::Object,
|
||||||
|
Gio::AsyncResult res) {
|
||||||
|
auto &sandbox = Core::Sandbox::Instance();
|
||||||
|
sandbox.customEnterFromEventLoop([&] {
|
||||||
|
const auto result
|
||||||
|
= _interface.call_notify_finish(res);
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
Gio::DBusErrorNS_::strip_remote_error(
|
||||||
|
result.error());
|
||||||
|
LOG(("Native Notification Error: %1").arg(
|
||||||
|
result.error().message_().c_str()));
|
||||||
|
clearNotification(notificationId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
weak->id = std::get<1>(*result);
|
||||||
|
});
|
||||||
|
})),
|
||||||
|
gi::scope_async);
|
||||||
|
|
||||||
|
xdg_notifications_notifications_call_notify(
|
||||||
|
_interface.gobj_(),
|
||||||
|
AppName.data(),
|
||||||
|
0,
|
||||||
|
(imageKey.empty() || !hints.lookup_value(imageKey)
|
||||||
|
? base::IconName().toStdString()
|
||||||
|
: std::string()).c_str(),
|
||||||
|
(hasBodyMarkup || info.subtitle.isEmpty()
|
||||||
|
? info.title.toStdString()
|
||||||
|
: info.subtitle.toStdString()
|
||||||
|
+ " (" + info.title.toStdString() + ')').c_str(),
|
||||||
|
(hasBodyMarkup
|
||||||
|
? info.subtitle.isEmpty()
|
||||||
|
? info.message.toHtmlEscaped().toStdString()
|
||||||
|
: u"<b>%1</b>\n%2"_q.arg(
|
||||||
|
info.subtitle.toHtmlEscaped(),
|
||||||
|
info.message.toHtmlEscaped()).toStdString()
|
||||||
|
: info.message.toStdString()).c_str(),
|
||||||
|
!actions.empty()
|
||||||
|
? (actions
|
||||||
|
| ranges::views::transform(&gi::cstring::c_str)
|
||||||
|
| ranges::to_vector).data()
|
||||||
|
: nullptr,
|
||||||
|
hints.end().gobj_(),
|
||||||
|
-1,
|
||||||
|
nullptr,
|
||||||
|
&callbackWrap->wrapper,
|
||||||
|
callbackWrap);
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void Manager::Private::clearAll() {
|
void Manager::Private::clearAll() {
|
||||||
for (const auto &[key, notifications] : base::take(_notifications)) {
|
for (const auto &[key, notifications] : base::take(_notifications)) {
|
||||||
for (const auto &[msgId, notification] : notifications) {
|
for (const auto &[msgId, notification] : notifications) {
|
||||||
|
const auto notificationId = NotificationId{
|
||||||
|
.contextId = key,
|
||||||
|
.msgId = msgId,
|
||||||
|
};
|
||||||
v::match(notification, [&](const std::string ¬ification) {
|
v::match(notification, [&](const std::string ¬ification) {
|
||||||
const auto notificationId = NotificationId{
|
|
||||||
.contextId = key,
|
|
||||||
.msgId = msgId,
|
|
||||||
};
|
|
||||||
_application.withdraw_notification(notification);
|
_application.withdraw_notification(notification);
|
||||||
clearNotification(notificationId);
|
|
||||||
}, [&](const Notification ¬ification) {
|
}, [&](const Notification ¬ification) {
|
||||||
notification->close();
|
_interface.call_close_notification(notification->id, nullptr);
|
||||||
});
|
});
|
||||||
|
clearNotification(notificationId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -879,10 +831,10 @@ void Manager::Private::clearFromItem(not_null<HistoryItem*> item) {
|
||||||
}
|
}
|
||||||
v::match(taken, [&](const std::string &taken) {
|
v::match(taken, [&](const std::string &taken) {
|
||||||
_application.withdraw_notification(taken);
|
_application.withdraw_notification(taken);
|
||||||
clearNotification(notificationId);
|
|
||||||
}, [&](const Notification &taken) {
|
}, [&](const Notification &taken) {
|
||||||
taken->close();
|
_interface.call_close_notification(taken->id, nullptr);
|
||||||
});
|
});
|
||||||
|
clearNotification(notificationId);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Manager::Private::clearFromTopic(not_null<Data::ForumTopic*> topic) {
|
void Manager::Private::clearFromTopic(not_null<Data::ForumTopic*> topic) {
|
||||||
|
@ -896,16 +848,16 @@ void Manager::Private::clearFromTopic(not_null<Data::ForumTopic*> topic) {
|
||||||
_notifications.erase(i);
|
_notifications.erase(i);
|
||||||
|
|
||||||
for (const auto &[msgId, notification] : temp) {
|
for (const auto &[msgId, notification] : temp) {
|
||||||
|
const auto notificationId = NotificationId{
|
||||||
|
.contextId = key,
|
||||||
|
.msgId = msgId,
|
||||||
|
};
|
||||||
v::match(notification, [&](const std::string ¬ification) {
|
v::match(notification, [&](const std::string ¬ification) {
|
||||||
const auto notificationId = NotificationId{
|
|
||||||
.contextId = key,
|
|
||||||
.msgId = msgId,
|
|
||||||
};
|
|
||||||
_application.withdraw_notification(notification);
|
_application.withdraw_notification(notification);
|
||||||
clearNotification(notificationId);
|
|
||||||
}, [&](const Notification ¬ification) {
|
}, [&](const Notification ¬ification) {
|
||||||
notification->close();
|
_interface.call_close_notification(notification->id, nullptr);
|
||||||
});
|
});
|
||||||
|
clearNotification(notificationId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -925,16 +877,16 @@ void Manager::Private::clearFromHistory(not_null<History*> history) {
|
||||||
i = _notifications.erase(i);
|
i = _notifications.erase(i);
|
||||||
|
|
||||||
for (const auto &[msgId, notification] : temp) {
|
for (const auto &[msgId, notification] : temp) {
|
||||||
|
const auto notificationId = NotificationId{
|
||||||
|
.contextId = key,
|
||||||
|
.msgId = msgId,
|
||||||
|
};
|
||||||
v::match(notification, [&](const std::string ¬ification) {
|
v::match(notification, [&](const std::string ¬ification) {
|
||||||
const auto notificationId = NotificationId{
|
|
||||||
.contextId = key,
|
|
||||||
.msgId = msgId,
|
|
||||||
};
|
|
||||||
_application.withdraw_notification(notification);
|
_application.withdraw_notification(notification);
|
||||||
clearNotification(notificationId);
|
|
||||||
}, [&](const Notification ¬ification) {
|
}, [&](const Notification ¬ification) {
|
||||||
notification->close();
|
_interface.call_close_notification(notification->id, nullptr);
|
||||||
});
|
});
|
||||||
|
clearNotification(notificationId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -950,16 +902,16 @@ void Manager::Private::clearFromSession(not_null<Main::Session*> session) {
|
||||||
i = _notifications.erase(i);
|
i = _notifications.erase(i);
|
||||||
|
|
||||||
for (const auto &[msgId, notification] : temp) {
|
for (const auto &[msgId, notification] : temp) {
|
||||||
|
const auto notificationId = NotificationId{
|
||||||
|
.contextId = key,
|
||||||
|
.msgId = msgId,
|
||||||
|
};
|
||||||
v::match(notification, [&](const std::string ¬ification) {
|
v::match(notification, [&](const std::string ¬ification) {
|
||||||
const auto notificationId = NotificationId{
|
|
||||||
.contextId = key,
|
|
||||||
.msgId = msgId,
|
|
||||||
};
|
|
||||||
_application.withdraw_notification(notification);
|
_application.withdraw_notification(notification);
|
||||||
clearNotification(notificationId);
|
|
||||||
}, [&](const Notification ¬ification) {
|
}, [&](const Notification ¬ification) {
|
||||||
notification->close();
|
_interface.call_close_notification(notification->id, nullptr);
|
||||||
});
|
});
|
||||||
|
clearNotification(notificationId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -988,10 +940,6 @@ Manager::Manager(not_null<Window::Notifications::System*> system)
|
||||||
, _private(std::make_unique<Private>(this)) {
|
, _private(std::make_unique<Private>(this)) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Manager::clearNotification(NotificationId id) {
|
|
||||||
_private->clearNotification(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
Manager::~Manager() = default;
|
Manager::~Manager() = default;
|
||||||
|
|
||||||
void Manager::doShowNativeNotification(
|
void Manager::doShowNativeNotification(
|
||||||
|
|
|
@ -15,7 +15,6 @@ namespace Notifications {
|
||||||
class Manager : public Window::Notifications::NativeManager {
|
class Manager : public Window::Notifications::NativeManager {
|
||||||
public:
|
public:
|
||||||
Manager(not_null<Window::Notifications::System*> system);
|
Manager(not_null<Window::Notifications::System*> system);
|
||||||
void clearNotification(NotificationId id);
|
|
||||||
~Manager();
|
~Manager();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
Loading…
Add table
Reference in a new issue