Skip to content

Commit

Permalink
fix various issues
Browse files Browse the repository at this point in the history
- rename app_icon to notification_icon since it is not necessarily the one for the app
- implemented an app_image that will be prefered over image (from notification not app) for Raven. notification popups will use one or the other depending on what is provided.
- broken notification grouping as it was assuming an app_name which can technically be empty when the application only supplies a desktop-entry for app info. use notification's app name otherwise try with the app id.
- missing application name when application only supplies desktop-entry
- incorrect image for some applications such as discord. it would use the Discord message sender's avatar rather than discord icon
- spotify reports as com.spotify.Client and not Spotify nowadays, so added that to list of spam-apps to prevent Raven noise
- missing printf on ngettext for header resulting in number not being added
- missing use_markup on notification title resulting in ampersands not being parsed. noticable in song titles on spotify where many use & instead of "feat."
  • Loading branch information
JoshStrobl committed Sep 30, 2023
1 parent 2459090 commit 6c0608b
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 44 deletions.
3 changes: 2 additions & 1 deletion src/daemon/notifications/popup.vala
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,8 @@ namespace Budgie.Notifications {
margin_bottom = 5,
margin_right = 16,
halign = Gtk.Align.START,
hexpand = true
hexpand = true,
use_markup = true
};
title_label.get_style_context().add_class("notification-title");

Expand Down
92 changes: 58 additions & 34 deletions src/lib/notification.vala
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,9 @@

/* Notification information */
public string app_name { get; construct; }
public string notification_icon { get; construct; }
public HashTable<string, Variant> hints { get; construct; }
public string[] actions { get; construct; }
public string app_icon { get; construct; }
public string body { get; construct set; }
public string summary { get; construct set; }
public uint expire_timeout { get; construct set; }
Expand All @@ -58,6 +58,7 @@
public int64 timestamp { get; construct set; }

/* Icon stuff */
public Gtk.Image? app_image { get; set; default = null; }
public Gtk.Image? image { get; set; default = null; }

private static Regex entity_regex;
Expand All @@ -66,7 +67,7 @@
public Notification(
uint32 id,
string app_name,
string app_icon,
string notification_icon,
string summary,
string body,
string[] actions,
Expand All @@ -75,14 +76,14 @@
) {
var name = app_name;

if (("budgie" in name) && ("caffeine" in app_icon)) { // Caffeine Notification
if (("budgie" in name) && ("caffeine" in notification_icon)) { // Caffeine Notification
name = _("Caffeine Mode");
}

Object (
id: id,
app_name: name,
app_icon: app_icon,
notification_icon: notification_icon,
summary: summary,
body: body,
actions: actions,
Expand All @@ -96,31 +97,35 @@
entity_regex = new Regex("&(?!amp;|quot;|apos;|lt;|gt;|#39;|nbsp;)");
tag_regex = new Regex("<(?!\\/?[biu]>)");
} catch (Error e) {
warning("Invalid notificiation regex: %s", e.message);
warning("Invalid notification regex: %s", e.message);
}
}

construct {
unowned Variant? variant = null;
this.timestamp = new DateTime.now().to_unix();
timestamp = new DateTime.now().to_unix();

// Set the priority
if ((variant = hints.lookup("urgency")) != null && variant.is_of_type(VariantType.BYTE)) {
this.urgency = (GLib.NotificationPriority) variant.get_byte();
urgency = (GLib.NotificationPriority) variant.get_byte();
}

// Set the category
if ((variant = hints.lookup("category")) != null && variant.is_of_type(VariantType.STRING)) {
this.category = variant.get_string();
category = variant.get_string();
}

// Set the application ID and app info
if ((variant = hints.lookup("desktop-entry")) != null && variant.is_of_type(VariantType.STRING)) {
this.app_id = variant.get_string();
app_id = variant.get_string();
app_id.replace(".desktop", "");
this.app_info = new DesktopAppInfo("%s.desktop".printf(app_id));
app_info = new DesktopAppInfo("%s.desktop".printf(app_id));

if (app_info != null) app_name = app_info.get_string("Name") ?? app_name;
}

app_image = get_appinfo_image(Gtk.IconSize.DND, app_id.down());

bool image_found = false;

// Per the Freedesktop Notification spec, first check if there is image data
Expand All @@ -136,42 +141,42 @@
}

// If there was no image data, check if we have a path to the image to use.
if (!image_found) {
if (
if (!image_found &&
(
(variant = hints.lookup("image-path")) != null ||
(variant = hints.lookup("image_path")) != null
) {
var path = variant.get_string();
)
) {
var path = variant.get_string();

if (Gtk.IconTheme.get_default().has_icon(path) && path != this.app_icon) {
var icon = new ThemedIcon(path);
this.image = new Gtk.Image.from_gicon(icon, Gtk.IconSize.DIALOG);
if (Gtk.IconTheme.get_default().has_icon(path) && path != notification_icon) {
var icon = new ThemedIcon(path);
image = new Gtk.Image.from_gicon(icon, Gtk.IconSize.DIALOG);
image_found = true;
} else if (path.has_prefix("/") || path.has_prefix("file://")) {
try {
var pixbuf = new Gdk.Pixbuf.from_file_at_size(path, 48, 48);
image = new Gtk.Image.from_pixbuf(pixbuf);
image_found = true;
} else if (path.has_prefix("/") || path.has_prefix("file://")) {
try {
var pixbuf = new Gdk.Pixbuf.from_file_at_size(path, 48, 48);
this.image = new Gtk.Image.from_pixbuf(pixbuf);
image_found = true;
} catch (Error e) {
critical("Unable to get pixbuf from path: %s", e.message);
}
} catch (Error e) {
critical("Unable to get pixbuf from path: %s", e.message);
}
}
}

// If no image path, try the app_icon parameter.
// If no image path, try the notification_icon parameter.
if (!image_found) {
if (app_icon != "" && !app_icon.contains("/")) { // Use the app icon directly
this.image = new Gtk.Image.from_icon_name(app_icon, Gtk.IconSize.DIALOG);
if (notification_icon != "" && !notification_icon.contains("/")) { // Use the app icon directly
image = new Gtk.Image.from_icon_name(notification_icon, Gtk.IconSize.DIALOG);
image_found = true;
} else if (app_icon == "" && app_info != null) { // Try to get icon from application info
this.image = new Gtk.Image.from_gicon(app_info.get_icon(), Gtk.IconSize.DIALOG);
} else if (notification_icon == "" && app_info != null) { // Try to get icon from application info
image = get_appinfo_image(Gtk.IconSize.DIALOG, "mail-unread-symbolic");
image_found = true;
} else if (app_icon.contains("/")) { // Try to get icon from file
var file = File.new_for_uri(app_icon);
} else if (notification_icon.contains("/")) { // Try to get icon from file
var file = File.new_for_uri(notification_icon);
if (file.query_exists()) {
var icon = new FileIcon(file);
this.image = new Gtk.Image.from_gicon(icon, Gtk.IconSize.DIALOG);
image = new Gtk.Image.from_gicon(icon, Gtk.IconSize.DIALOG);
image_found = true;
}
}
Expand All @@ -188,7 +193,7 @@

// If we still don't have a valid image to use, show a generic icon
if (!image_found) {
this.image = new Gtk.Image.from_icon_name("mail-unread-symbolic", Gtk.IconSize.DIALOG);
image = new Gtk.Image.from_icon_name("mail-unread-symbolic", Gtk.IconSize.DIALOG);
}

// GLib.Notification only requires summary, so make sure we have a title
Expand Down Expand Up @@ -247,5 +252,24 @@

return text;
}

private Gtk.Image? get_appinfo_image(Gtk.IconSize size, string? fallback) {
if (app_info == null) {
var fallback_image = new Gtk.Image.from_icon_name(fallback, size);
var invalid_image = (fallback_image == null) || (fallback_image.icon_name == null) || (fallback_image.icon_name == "image-missing") || (fallback_image.icon_name == "");
return invalid_image ? null : fallback_image;
}

var app_icon_name = app_info.get_string("Icon"); // Use the Icon from the respective DesktopAppInfo or fallback to generic applications-internet
var app_icon = app_info.get_icon();

if (app_icon_name != null) {
return new Gtk.Image.from_icon_name(app_icon_name, size);
} else if ((app_icon_name == null) && (app_icon != null)) {
return new Gtk.Image.from_gicon(app_icon, size);
}

return null;
}
}
}
2 changes: 1 addition & 1 deletion src/panel/com.solus-project.budgie-panel.gschema.xml
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@
</key>

<key type="as" name="spam-apps">
<default>['Spotify', 'Lollypop', 'audacious', 'org.buddiesofbudgie.BudgieDesktopNmApplet.desktop', 'lollypop.desktop', 'budgie-power-panel', 'budgie-printer-panel', 'gnome-power-panel', 'gnome-printer-panel', 'nm-applet']</default>
<default>['Spotify', 'Lollypop', 'audacious', 'com.spotify.Client', 'org.buddiesofbudgie.BudgieDesktopNmApplet', 'lollypop', 'budgie-power-panel', 'budgie-printer-panel', 'gnome-power-panel', 'gnome-printer-panel', 'nm-applet']</default>
<summary>Spam Apps</summary>
<description>Notifications send by these apps will not be displayed in Raven.</description>
</key>
Expand Down
2 changes: 1 addition & 1 deletion src/raven/notifications_group.vala
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ namespace Budgie {
public NotificationGroup(Budgie.Notification notification, NotificationSort sort_mode, uint keep) {
Object(
app_name: notification.app_name,
image: notification.image,
image: notification.app_image ?? notification.image ?? new Gtk.Image.from_icon_name("applications-internet", Gtk.IconSize.DND),
tokeep: keep,
noti_sort_mode: sort_mode,
activatable: false,
Expand Down
16 changes: 9 additions & 7 deletions src/raven/notifications_view.vala
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ namespace Budgie {

this.notifications[id] = notification;

string settings_app_name = app_name;
string settings_app_name = notification.app_name;

// If this notification has a desktop entry in the hints,
// set the app name to get the settings for to it.
Expand All @@ -240,7 +240,7 @@ namespace Budgie {
!application_settings.get_boolean("show-banners");

if (no_popup) {
on_notification_closed(id, app_name, NotificationCloseReason.EXPIRED);
on_notification_closed(id, notification.app_name, NotificationCloseReason.EXPIRED);
}
}

Expand All @@ -255,14 +255,14 @@ namespace Budgie {
string[] spam_apps = budgie_settings.get_strv(Budgie.ROOT_KEY_SPAM_APPS);
string[] spam_categories = budgie_settings.get_strv(Budgie.ROOT_KEY_SPAM_CATEGORIES);

var app_id = (notification.app_id != null) ? notification.app_id : app_name;
var app_id = notification.app_id ?? notification.app_name;
bool should_store = !(notification.category != null && notification.category in spam_categories) &&
!(app_id != null && app_id in spam_apps);
!(app_id in spam_apps);

if (!should_store) return;

// Look for an existing group. If one doesn't exist, create it
var group = get_notification_group(app_name);
var group = get_notification_group(notification.app_name) ?? get_notification_group(notification.app_id);
if (group == null) {
group = new NotificationGroup(notification, sort_mode, max_per_group);
listbox.add(group);
Expand All @@ -289,7 +289,9 @@ namespace Budgie {
Raven.get_instance().UnreadNotifications();
}

private NotificationGroup? get_notification_group(string name) {
private NotificationGroup? get_notification_group(string? name) {
if (name == null) return null;

foreach (var group in listbox.get_children()) {
if (((NotificationGroup) group).app_name == name) {
return group as NotificationGroup;
Expand Down Expand Up @@ -336,7 +338,7 @@ namespace Budgie {
string? text = null;

if (notification_count > 0) {
text = ngettext("%u unread notification", "%u unread notifications", notification_count);
text = (ngettext("%u unread notification", "%u unread notifications", notification_count)).printf(notification_count);
} else {
text = _("No unread notifications");
}
Expand Down

0 comments on commit 6c0608b

Please sign in to comment.