feat(notifications): implement actions for Linux (#5976)

This commit is contained in:
Arne
2025-03-02 16:07:52 +01:00
committed by GitHub
parent 13dfe716b8
commit 1812f1bb15
3 changed files with 103 additions and 23 deletions
+1 -1
View File
@@ -9,7 +9,7 @@
- Minor: When (re-)connecting, visible channels are now joined first. (#5850)
- Minor: Added the ability to filter on messages by the author's user ID (example: `author.user_id == "22484632"`). (#5862)
- Minor: Improved error messaging of the `/clip` command. (#5879)
- Minor: Added Linux support for Live Notifications toasts. (#5881, #5971)
- Minor: Added Linux support for Live Notifications toasts. (#5881, #5971, #5976)
- Minor: Messages can now be deleted from the context menu in a channel. (#5956)
- Minor: Overlay windows now inherit the global zoom level and can be zoomed independently. (#6016)
- Bugfix: Fixed a potential way to escape the Lua Plugin sandbox. (#5846)
+101 -20
View File
@@ -63,6 +63,67 @@ Q_SIGNALS:
void downloadComplete();
};
void performReaction(const ToastReaction &reaction, const QString &channelName)
{
switch (reaction)
{
case ToastReaction::OpenInBrowser:
QDesktopServices::openUrl(
QUrl(u"https://www.twitch.tv/" % channelName));
break;
case ToastReaction::OpenInPlayer:
QDesktopServices::openUrl(QUrl(TWITCH_PLAYER_URL.arg(channelName)));
break;
case ToastReaction::OpenInStreamlink: {
openStreamlinkForChannel(channelName);
break;
}
case ToastReaction::DontOpen:
// nothing should happen
break;
}
}
#ifdef CHATTERINO_WITH_LIBNOTIFY
void onAction(NotifyNotification *notif, const char *actionRaw, void *userData)
{
QString action(actionRaw);
auto *channelName = static_cast<QString *>(userData);
// by default we perform the action that is specified in the settings
auto toastReaction =
static_cast<ToastReaction>(getSettings()->openFromToast.getValue());
if (action == OPEN_IN_BROWSER)
{
toastReaction = ToastReaction::OpenInBrowser;
}
else if (action == OPEN_PLAYER_IN_BROWSER)
{
toastReaction = ToastReaction::OpenInPlayer;
}
else if (action == OPEN_IN_STREAMLINK)
{
toastReaction = ToastReaction::OpenInStreamlink;
}
performReaction(toastReaction, *channelName);
notify_notification_close(notif, nullptr);
}
void onActionClosed(NotifyNotification *notif, void * /*userData*/)
{
g_object_unref(notif);
}
void onNotificationDestroyed(void *data)
{
auto *channelNameHeap = static_cast<QString *>(data);
delete channelNameHeap;
}
#endif
} // namespace
namespace chatterino {
@@ -182,24 +243,7 @@ public:
auto toastReaction =
static_cast<ToastReaction>(getSettings()->openFromToast.getValue());
switch (toastReaction)
{
case ToastReaction::OpenInBrowser:
QDesktopServices::openUrl(
QUrl(u"https://www.twitch.tv/" % channelName_));
break;
case ToastReaction::OpenInPlayer:
QDesktopServices::openUrl(
QUrl(TWITCH_PLAYER_URL.arg(channelName_)));
break;
case ToastReaction::OpenInStreamlink: {
openStreamlinkForChannel(channelName_);
break;
}
case ToastReaction::DontOpen:
// nothing should happen
break;
}
performReaction(toastReaction, channelName_);
}
void toastActivated(int actionIndex) const override
@@ -310,6 +354,38 @@ void Toasts::sendLibnotify(const QString &channelName,
NotifyNotification *notif = notify_notification_new(
str.toUtf8().constData(), channelTitle.toUtf8().constData(), nullptr);
// this will be freed in onNotificationDestroyed
auto *channelNameHeap = new QString(channelName);
// we only set onNotificationDestroyed as free_func in the first action
// because all free_funcs will be called once the notification is destroyed
// which would cause a double-free otherwise
notify_notification_add_action(notif, OPEN_IN_BROWSER.toUtf8().constData(),
OPEN_IN_BROWSER.toUtf8().constData(),
(NotifyActionCallback)onAction,
channelNameHeap, onNotificationDestroyed);
notify_notification_add_action(
notif, OPEN_PLAYER_IN_BROWSER.toUtf8().constData(),
OPEN_PLAYER_IN_BROWSER.toUtf8().constData(),
(NotifyActionCallback)onAction, channelNameHeap, nullptr);
notify_notification_add_action(
notif, OPEN_IN_STREAMLINK.toUtf8().constData(),
OPEN_IN_STREAMLINK.toUtf8().constData(), (NotifyActionCallback)onAction,
channelNameHeap, nullptr);
auto defaultToastReaction =
static_cast<ToastReaction>(getSettings()->openFromToast.getValue());
if (defaultToastReaction != ToastReaction::DontOpen)
{
notify_notification_add_action(
notif, "default",
Toasts::findStringFromReaction(defaultToastReaction)
.toUtf8()
.constData(),
(NotifyActionCallback)onAction, channelNameHeap, nullptr);
}
GdkPixbuf *img = gdk_pixbuf_new_from_file(
avatarFilePath(channelName).toUtf8().constData(), nullptr);
if (img == nullptr)
@@ -322,8 +398,13 @@ void Toasts::sendLibnotify(const QString &channelName,
g_object_unref(img);
}
notify_notification_show(notif, nullptr);
g_object_unref(notif);
g_signal_connect(notif, "closed", (GCallback)onActionClosed, nullptr);
gboolean success = notify_notification_show(notif, nullptr);
if (success == 0)
{
g_object_unref(notif);
}
}
#endif
@@ -49,8 +49,7 @@ NotificationPage::NotificationPage()
#if defined(Q_OS_WIN) || defined(CHATTERINO_WITH_LIBNOTIFY)
settings.append(this->createCheckBox(
"Show notification", getSettings()->notificationToast));
#endif
#ifdef Q_OS_WIN
auto openIn = settings.emplace<QHBoxLayout>().withoutMargin();
{
openIn