Push notifications

This commit is contained in:
bleakgrey 2018-05-16 14:23:48 +03:00
parent ae1d615803
commit 44aa22c06e
7 changed files with 99 additions and 16 deletions

View File

@ -10,3 +10,4 @@ Categories=GNOME;GTK;Network;
Keywords=toot;mastodon;social;network;post; Keywords=toot;mastodon;social;network;post;
MimeType=text/plain; MimeType=text/plain;
StartupNotify=true StartupNotify=true
X-GNOME-UsesNotifications=true

View File

@ -55,6 +55,23 @@ public enum Tootle.NotificationType {
assert_not_reached(); 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 () { public string get_icon () {
switch (this) { switch (this) {

View File

@ -5,8 +5,12 @@ using Json;
public class Tootle.NetManager : GLib.Object { public class Tootle.NetManager : GLib.Object {
public abstract signal void started(); public abstract signal void started ();
public abstract signal void finished(); 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 int requests_processing = 0;
private Soup.Session session; private Soup.Session session;
@ -49,7 +53,7 @@ public class Tootle.NetManager : GLib.Object {
return; return;
} }
notificator = new Notificator (acc); notificator = new Notificator (acc);
//notificator.start (); notificator.start ();
}); });
} }
@ -85,11 +89,9 @@ public class Tootle.NetManager : GLib.Object {
default: default:
break; break;
} }
if (cb != null) if (cb != null)
cb (sess, mess); cb (sess, mess);
msg.request_body.free ();
msg.response_body.free ();
}); });
return msg; return msg;
} }

View File

@ -14,10 +14,8 @@ public class Tootle.Notificator : GLib.Object {
public async void start () { public async void start () {
var msg = account.get_stream (); var msg = account.get_stream ();
connection = yield Tootle.network.stream (msg); connection = yield Tootle.network.stream (msg);
connection.error.connect (e => error (e.message)); connection.error.connect (on_error);
connection.message.connect ((i, bytes) => { connection.message.connect (on_message);
warning ((string)bytes.get_data ());
});
debug ("Receiving notifications for %lld", account.id); debug ("Receiving notifications for %lld", account.id);
} }
@ -26,4 +24,56 @@ public class Tootle.Notificator : GLib.Object {
connection.close (0, null); 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);
}
} }

View File

@ -9,6 +9,7 @@ public class Tootle.NotificationsView : Tootle.AbstractView {
view.remove.connect (on_remove); view.remove.connect (on_remove);
Tootle.accounts.switched.connect(on_account_changed); Tootle.accounts.switched.connect(on_account_changed);
Tootle.app.refresh.connect(on_refresh); Tootle.app.refresh.connect(on_refresh);
Tootle.network.notification.connect (notification => prepend (notification));
request (); request ();
} }
@ -21,18 +22,18 @@ public class Tootle.NotificationsView : Tootle.AbstractView {
return _("Notifications"); return _("Notifications");
} }
public void prepend(Notification notification){ public void prepend (Notification notification, bool invert_order = false) {
var separator = new Gtk.Separator (Gtk.Orientation.HORIZONTAL); var separator = new Gtk.Separator (Gtk.Orientation.HORIZONTAL);
separator.show (); separator.show ();
var widget = new NotificationWidget(notification); var widget = new NotificationWidget(notification);
widget.separator = separator; widget.separator = separator;
image.icon_name = "notification-new-symbolic";
view.pack_start(separator, false, false, 0); view.pack_start(separator, false, false, 0);
view.pack_start(widget, 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)) if (!(widget is NotificationWidget))
return; return;
@ -40,19 +41,19 @@ public class Tootle.NotificationsView : Tootle.AbstractView {
image.icon_name = get_icon (); image.icon_name = get_icon ();
} }
public virtual void on_refresh (){ public virtual void on_refresh () {
clear (); clear ();
request (); request ();
} }
public virtual void on_account_changed (Account? account){ public virtual void on_account_changed (Account? account) {
if(account == null) if(account == null)
return; return;
on_refresh (); on_refresh ();
} }
public void request (){ public void request () {
var url = "%s/api/v1/follow_requests".printf (Tootle.settings.instance_url); var url = "%s/api/v1/follow_requests".printf (Tootle.settings.instance_url);
var msg = new Soup.Message("GET", url); var msg = new Soup.Message("GET", url);
Tootle.network.queue(msg, (sess, mess) => { Tootle.network.queue(msg, (sess, mess) => {

View File

@ -40,6 +40,13 @@ public class Tootle.NotificationWidget : Gtk.Grid {
label.label = notification.type.get_desc (notification.account); label.label = notification.type.get_desc (notification.account);
get_style_context ().add_class ("notification"); get_style_context ().add_class ("notification");
if (notification.status != null) {
Tootle.network.status_removed.connect (id => {
if (id == notification.status.id)
destroy ();
});
}
destroy.connect (() => { destroy.connect (() => {
if(separator != null) if(separator != null)
separator.destroy (); separator.destroy ();

View File

@ -179,6 +179,11 @@ public class Tootle.StatusWidget : Gtk.EventBox {
separator.destroy (); separator.destroy ();
}); });
Tootle.network.status_removed.connect (id => {
if (id == status.id)
destroy ();
});
rebind (); rebind ();
} }