Skip to content

Commit

Permalink
Support buttons with action group (#263)
Browse files Browse the repository at this point in the history
  • Loading branch information
leolost2605 authored Aug 29, 2023
1 parent 40545f6 commit 35c0889
Show file tree
Hide file tree
Showing 6 changed files with 102 additions and 25 deletions.
3 changes: 2 additions & 1 deletion src/Indicator.vala
Original file line number Diff line number Diff line change
Expand Up @@ -195,12 +195,13 @@ public class Notifications.Indicator : Wingpanel.Indicator {

private void on_notification_closed (uint32 id, Notification.CloseReason reason) {
SearchFunc<NotificationEntry, uint32> find_entry = (e, i) => {
return i == e.notification.id ? 0 : i > e.notification.id ? 1 : -1;
return i == e.notification.server_id ? 0 : i > e.notification.server_id ? 1 : -1;
};

foreach (var app_entry in nlist.app_entries.values) {
unowned var node = app_entry.app_notifications.search (id, find_entry);
if (node != null) {
node.data.notification.server_id = 0; // Notification is now outdated
node.data.clear ();
return;
}
Expand Down
45 changes: 40 additions & 5 deletions src/Services/Notification.vala
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,21 @@ public class Notifications.Notification : Object {
UNDEFINED = 4
}

public const string DEFAULT_ACTION = "default";
public const string DESKTOP_ID_EXT = ".desktop";

public string internal_id { get; construct set; } // Format: "timestamp.server_id"
public string app_name;
public string summary;
public string message_body;
public string image_path { get; private set; default = ""; }
public string app_icon;
public string sender;
public string[] actions;
public List<Gtk.Button> buttons;
public string? default_action { get; private set; default = null; }
public uint32 replaces_id;
public uint32 id;
public uint32 server_id { get; construct set; default = 0; } // 0 means the notification is outdated i.e. not present in the server anymore
public bool has_temp_file;
public GLib.DateTime timestamp;
public GLib.Icon badge_icon { get; construct set; }
Expand All @@ -53,25 +57,26 @@ public class Notifications.Notification : Object {
COUNT
}

private const string DEFAULT_ACTION = "default";
private const string X_CANONICAL_PRIVATE_KEY = "x-canonical-private-synchronous";
private const string DESKTOP_ENTRY_KEY = "desktop-entry";
private const string FALLBACK_DESKTOP_ID = "gala-other" + DESKTOP_ID_EXT;

public Notification (
uint32 _id, string _app_name, string _app_icon, string _summary, string _message_body, string _image_path,
string _internal_id, string _app_name, string _app_icon, string _summary, string _message_body, string _image_path,
string[] _actions, string _desktop_id, int64 _unix_time, uint64 _replaces_id, string _sender, bool _has_temp_file
) {
internal_id = _internal_id;
app_name = _app_name;
app_icon = _app_icon;
summary = _summary;
message_body = _message_body;
image_path = _image_path;
replaces_id = (uint32) _replaces_id;
id = _id;
sender = _sender;

actions = _actions;
buttons = validate_actions (actions);

timestamp = new GLib.DateTime.from_unix_local (_unix_time);

desktop_id = _desktop_id;
Expand All @@ -88,12 +93,16 @@ public class Notifications.Notification : Object {
message_body = get_string (body, Column.BODY);
var hints = body.get_child_value (Column.HINTS);
replaces_id = get_uint32 (body, Column.REPLACES_ID);
id = _id;
server_id = _id;
sender = message.get_sender ();

actions = body.get_child_value (Column.ACTIONS).dup_strv ();
buttons = validate_actions (actions);

timestamp = new GLib.DateTime.now_local ();

internal_id = timestamp.to_unix ().to_string () + "." + server_id.to_string ();

desktop_id = lookup_string (hints, DESKTOP_ENTRY_KEY);
if (desktop_id != null && desktop_id != "") {
if (!desktop_id.has_suffix (DESKTOP_ID_EXT)) {
Expand Down Expand Up @@ -140,6 +149,32 @@ public class Notifications.Notification : Object {
}
}

private List<Gtk.Button> validate_actions (string[] actions) {
var list = new List<Gtk.Button> ();

for (int i = 0; i < actions.length; i += 2) {
if (actions[i] == DEFAULT_ACTION) {
default_action = server_id.to_string () + "." + DEFAULT_ACTION;
continue;
}

var label = actions[i + 1].strip ();
if (label == "") {
warning ("Action '%s' sent without label, skipping…", actions[i]);
continue;
}

var button = new Gtk.Button.with_label (label) {
action_name = NotificationsList.ACTION_PREFIX + server_id.to_string () + "." + actions[i],
width_request = 86
};

list.append (button);
}

return list;
}

private string get_string (Variant tuple, int column) {
var child = tuple.get_child_value (column);
return child.dup_string ();
Expand Down
18 changes: 15 additions & 3 deletions src/Services/NotificationsMonitor.vala
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,16 @@ public sealed class Notifications.NotificationsMonitor : Object {
private Gee.Map<uint32, DBusMessage> awaiting = new Gee.HashMap<uint32, DBusMessage> ();
private DBusConnection connection;

public DBusActionGroup? notifications_action_group { get; private set; }

construct {
notifications_action_group = DBusActionGroup.get (
Application.get_default ().get_dbus_connection (),
"org.freedesktop.Notifications",
"/org/freedesktop/Notifications"
);
}

public async void init () throws Error {
#if VALA_0_54
string address = BusType.SESSION.get_address_sync ();
Expand Down Expand Up @@ -74,13 +84,15 @@ public sealed class Notifications.NotificationsMonitor : Object {

case "CloseNotification":
case "NotificationClosed":
case "ActionInvoked":
Notification.CloseReason reason;
uint32 id;
uint32 id = body.get_child_value (0).get_uint32 ();

if (message.get_member () == "NotificationClosed") {
body.get ("(uu)", out id, out reason);
reason = (Notification.CloseReason) body.get_child_value (1).get_uint32 ();
} else if (message.get_member () == "ActionInvoked") {
reason = UNDEFINED;
} else {
id = body.get_child_value (0).get_uint32 ();
reason = CLOSE_NOTIFICATION_CALL;
}

Expand Down
7 changes: 4 additions & 3 deletions src/Services/Session.vala
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public class Notifications.Session : GLib.Object {
key.load_from_file (session_file.get_path (), KeyFileFlags.NONE);
foreach (unowned string group in key.get_groups ()) {
var notification = new Notification (
(uint32)int.parse (group),
group,
key.get_string (group, APP_NAME_KEY),
key.get_string (group, APP_ICON_KEY),
key.get_string (group, SUMMARY_KEY),
Expand All @@ -82,6 +82,7 @@ public class Notifications.Session : GLib.Object {
}
} catch (KeyFileError e) {
warning (e.message);
create_session_file ();
} catch (FileError e) {
warning (e.message);
}
Expand All @@ -91,7 +92,7 @@ public class Notifications.Session : GLib.Object {
}

public void add_notification (Notification notification, bool write_file = true) {
string id = notification.id.to_string ();
string id = notification.internal_id;
key.set_int64 (id, UNIX_TIME_KEY, notification.timestamp.to_unix ());
key.set_string (id, APP_ICON_KEY, notification.app_icon);
key.set_string (id, APP_NAME_KEY, notification.app_name);
Expand Down Expand Up @@ -119,7 +120,7 @@ public class Notifications.Session : GLib.Object {
critical ("Unable to delete tmpfile: %s", notification.image_path);
}
}
key.remove_group (notification.id.to_string ());
key.remove_group (notification.internal_id);
} catch (KeyFileError e) {
warning (e.message);
}
Expand Down
24 changes: 21 additions & 3 deletions src/Widgets/NotificationEntry.vala
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,8 @@ public class Notifications.NotificationEntry : Gtk.ListBoxRow {
delete_button.get_style_context ().add_provider (provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION);

var delete_revealer = new Gtk.Revealer () {
halign = Gtk.Align.START,
valign = Gtk.Align.START,
reveal_child = false,
transition_duration = Granite.TRANSITION_DURATION_CLOSE,
transition_type = Gtk.RevealerTransitionType.CROSSFADE
Expand Down Expand Up @@ -186,6 +188,19 @@ public class Notifications.NotificationEntry : Gtk.ListBoxRow {

grid.attach (body_label, 1, 1, 2);

if (notification.buttons.length () > 0) {
var action_area = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 6) {
margin_top = 12,
halign = Gtk.Align.END,
homogeneous = true
};
grid.attach (action_area, 0, 2, 3);

foreach (var button in notification.buttons) {
action_area.pack_end (button);
};
}

var delete_left = new DeleteAffordance (Gtk.Align.END) {
// Have to match with the grid
margin_top = 9,
Expand Down Expand Up @@ -231,9 +246,7 @@ public class Notifications.NotificationEntry : Gtk.ListBoxRow {

show_all ();

delete_button.clicked.connect (() => {
clear ();
});
delete_button.clicked.connect (() => clear ());

eventbox.enter_notify_event.connect ((event) => {
delete_revealer.reveal_child = true;
Expand Down Expand Up @@ -272,6 +285,11 @@ public class Notifications.NotificationEntry : Gtk.ListBoxRow {
}
});
revealer.reveal_child = false;

if (notification.server_id > 0) {
unowned var action_group = get_action_group (NotificationsList.ACTION_GROUP_PREFIX);
action_group.activate_action ("close", new Variant.array (VariantType.UINT32, { notification.server_id }));
}
}

private class DeleteAffordance : Gtk.Grid {
Expand Down
30 changes: 20 additions & 10 deletions src/Widgets/NotificationsList.vala
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
public class Notifications.NotificationsList : Gtk.ListBox {
public signal void close_popover ();

public const string ACTION_GROUP_PREFIX = "notifications-list";
public const string ACTION_PREFIX = ACTION_GROUP_PREFIX + ".";

public Gee.HashMap<string, AppEntry> app_entries { get; private set; }

private HashTable<string, int> table;
Expand All @@ -43,6 +46,8 @@ public class Notifications.NotificationsList : Gtk.ListBox {
set_placeholder (placeholder);
show_all ();

insert_action_group (ACTION_GROUP_PREFIX, new NotificationsMonitor ().notifications_action_group);

row_activated.connect (on_row_activated);
}

Expand Down Expand Up @@ -129,17 +134,22 @@ public class Notifications.NotificationsList : Gtk.ListBox {

private void on_row_activated (Gtk.ListBoxRow row) {
if (row is NotificationEntry) {
unowned NotificationEntry notification_entry = (NotificationEntry) row;
var context = notification_entry.get_display ().get_app_launch_context ();

if (notification_entry.notification.app_info != null) try {
notification_entry.notification.app_info.launch (null, context);
} catch (Error e) {
critical ("Unable to launch app: %s", e.message);
unowned var notification_entry = (NotificationEntry) row;

if (notification_entry.notification.default_action != null) {
unowned var action_group = get_action_group (ACTION_GROUP_PREFIX);
action_group.activate_action (notification_entry.notification.default_action, null);
close_popover ();
} else {
try {
var context = notification_entry.get_display ().get_app_launch_context ();
notification_entry.notification.app_info.launch (null, context);
notification_entry.clear ();
close_popover ();
} catch (Error e) {
warning ("Unable to launch app: %s", e.message);
}
}

notification_entry.clear ();
close_popover ();
}
}
}

0 comments on commit 35c0889

Please sign in to comment.