Push notifications
This commit is contained in:
parent
ae1d615803
commit
44aa22c06e
|
@ -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
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) => {
|
||||||
|
|
|
@ -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 ();
|
||||||
|
|
|
@ -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 ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue