diff --git a/data/com.github.bleakgrey.tootle.desktop.in b/data/com.github.bleakgrey.tootle.desktop.in index 9eead9a..1ed3814 100644 --- a/data/com.github.bleakgrey.tootle.desktop.in +++ b/data/com.github.bleakgrey.tootle.desktop.in @@ -10,3 +10,4 @@ Categories=GNOME;GTK;Network; Keywords=toot;mastodon;social;network;post; MimeType=text/plain; StartupNotify=true +X-GNOME-UsesNotifications=true diff --git a/src/API/NotificationType.vala b/src/API/NotificationType.vala index 0aeba37..bfe3318 100644 --- a/src/API/NotificationType.vala +++ b/src/API/NotificationType.vala @@ -55,6 +55,23 @@ public enum Tootle.NotificationType { assert_not_reached(); } } + + public string get_desc_plain (Account? account) { + switch (this) { + case MENTION: + return _("%s mentioned you").printf (account.display_name); + case REBLOG: + return _("%s boosted your toot").printf (account.display_name); + case FAVORITE: + return _("%s favorited your toot").printf (account.display_name); + case FOLLOW: + return _("%s now follows you").printf (account.display_name); + case FOLLOW_REQUEST: + return _("%s wants to follow you").printf (account.display_name); + default: + assert_not_reached(); + } + } public string get_icon () { switch (this) { diff --git a/src/NetManager.vala b/src/NetManager.vala index 1dcbde9..85cefeb 100644 --- a/src/NetManager.vala +++ b/src/NetManager.vala @@ -5,8 +5,12 @@ using Json; public class Tootle.NetManager : GLib.Object { - public abstract signal void started(); - public abstract signal void finished(); + public abstract signal void started (); + public abstract signal void finished (); + + public abstract signal void notification (Notification notification); + public abstract signal void status_added (Status status); + public abstract signal void status_removed (int64 id); private int requests_processing = 0; private Soup.Session session; @@ -49,7 +53,7 @@ public class Tootle.NetManager : GLib.Object { return; } notificator = new Notificator (acc); - //notificator.start (); + notificator.start (); }); } @@ -85,11 +89,9 @@ public class Tootle.NetManager : GLib.Object { default: break; } + if (cb != null) cb (sess, mess); - - msg.request_body.free (); - msg.response_body.free (); }); return msg; } diff --git a/src/Notificator.vala b/src/Notificator.vala index cda4e4a..9e51f60 100644 --- a/src/Notificator.vala +++ b/src/Notificator.vala @@ -14,10 +14,8 @@ public class Tootle.Notificator : GLib.Object { public async void start () { var msg = account.get_stream (); connection = yield Tootle.network.stream (msg); - connection.error.connect (e => error (e.message)); - connection.message.connect ((i, bytes) => { - warning ((string)bytes.get_data ()); - }); + connection.error.connect (on_error); + connection.message.connect (on_message); debug ("Receiving notifications for %lld", account.id); } @@ -26,4 +24,56 @@ public class Tootle.Notificator : GLib.Object { connection.close (0, null); } + private void on_error (Error e) { + error (e.message); + } + + private void on_message (int i, Bytes bytes) { + var network = Tootle.network; + var msg = (string) bytes.get_data (); + + var parser = new Json.Parser (); + parser.load_from_data (msg, -1); + var root = parser.get_root ().get_object (); + + var type = root.get_string_member ("event"); + switch (type) { + case "update": + var status = Status.parse (sanitize (root)); + network.status_added (status); + break; + case "delete": + var id = int64.parse (root.get_string_member("payload")); + network.status_removed (id); + break; + case "notification": + var notif = Notification.parse (sanitize (root)); + toast (notif); + network.notification (notif); + break; + default: + warning ("Unknown push event: %s", type); + break; + } + + } + + private Json.Object sanitize (Json.Object root) { + var payload = root.get_string_member ("payload"); + var sanitized = Soup.URI.decode (payload); + var parser = new Json.Parser (); + parser.load_from_data (sanitized, -1); + return parser.get_root ().get_object (); + } + + private void toast (Notification obj) { + var notification = new GLib.Notification (obj.type.get_desc_plain (obj.account)); + if (obj.status != null) { + var desc = obj.status.content; + Regex tags = new Regex("<(.|\n)*?>", RegexCompileFlags.CASELESS); + notification.set_body (tags.replace(desc, -1, 0, "")); + } + Tootle.app.send_notification (Tootle.app.application_id + ":" + obj.id.to_string (), notification); + } + } diff --git a/src/Views/NotificationsView.vala b/src/Views/NotificationsView.vala index 0713ba5..ef958b2 100644 --- a/src/Views/NotificationsView.vala +++ b/src/Views/NotificationsView.vala @@ -9,6 +9,7 @@ public class Tootle.NotificationsView : Tootle.AbstractView { view.remove.connect (on_remove); Tootle.accounts.switched.connect(on_account_changed); Tootle.app.refresh.connect(on_refresh); + Tootle.network.notification.connect (notification => prepend (notification)); request (); } @@ -21,18 +22,18 @@ public class Tootle.NotificationsView : Tootle.AbstractView { return _("Notifications"); } - public void prepend(Notification notification){ + public void prepend (Notification notification, bool invert_order = false) { var separator = new Gtk.Separator (Gtk.Orientation.HORIZONTAL); separator.show (); var widget = new NotificationWidget(notification); widget.separator = separator; + image.icon_name = "notification-new-symbolic"; view.pack_start(separator, false, false, 0); view.pack_start(widget, false, false, 0); - image.icon_name = "notification-new-symbolic"; } - public virtual void on_remove (Widget widget){ + public virtual void on_remove (Widget widget) { if (!(widget is NotificationWidget)) return; @@ -40,19 +41,19 @@ public class Tootle.NotificationsView : Tootle.AbstractView { image.icon_name = get_icon (); } - public virtual void on_refresh (){ + public virtual void on_refresh () { clear (); request (); } - public virtual void on_account_changed (Account? account){ + public virtual void on_account_changed (Account? account) { if(account == null) return; on_refresh (); } - public void request (){ + public void request () { var url = "%s/api/v1/follow_requests".printf (Tootle.settings.instance_url); var msg = new Soup.Message("GET", url); Tootle.network.queue(msg, (sess, mess) => { diff --git a/src/Widgets/NotificationWidget.vala b/src/Widgets/NotificationWidget.vala index 3de36fc..5bf2ea8 100644 --- a/src/Widgets/NotificationWidget.vala +++ b/src/Widgets/NotificationWidget.vala @@ -40,6 +40,13 @@ public class Tootle.NotificationWidget : Gtk.Grid { label.label = notification.type.get_desc (notification.account); get_style_context ().add_class ("notification"); + if (notification.status != null) { + Tootle.network.status_removed.connect (id => { + if (id == notification.status.id) + destroy (); + }); + } + destroy.connect (() => { if(separator != null) separator.destroy (); diff --git a/src/Widgets/StatusWidget.vala b/src/Widgets/StatusWidget.vala index dabcb81..dff9c0d 100644 --- a/src/Widgets/StatusWidget.vala +++ b/src/Widgets/StatusWidget.vala @@ -179,6 +179,11 @@ public class Tootle.StatusWidget : Gtk.EventBox { separator.destroy (); }); + Tootle.network.status_removed.connect (id => { + if (id == status.id) + destroy (); + }); + rebind (); }