1
0
mirror of https://gitlab.gnome.org/World/tootle synced 2025-02-17 03:51:11 +01:00

Convert NotificationsView into Timeline

This commit is contained in:
Bleak Grey 2020-05-30 19:24:11 +03:00
parent 7faca4090e
commit d79023b605
7 changed files with 115 additions and 162 deletions

View File

@ -3,9 +3,6 @@ project('com.github.bleakgrey.tootle', 'vala', 'c')
gnome = import('gnome')
i18n = import('i18n')
#add_project_arguments(['--disable-warnings', '-g', '-X', '-rdynamic'], language: 'vala')
#add_project_arguments(['-g', '-rdynamic', '-export-dynamic'], language: 'c')
add_global_arguments([
'-DGETTEXT_PACKAGE="@0@"'.format(meson.project_name()),
'-DHANDY_USE_UNSTABLE_API',
@ -82,7 +79,6 @@ executable(
dependency('libhandy-0.0', version: '>=0.0.13'),
],
install: true,
link_args: '-export-dynamic'
)
subdir('data')

View File

@ -45,6 +45,8 @@ public class Tootle.InstanceAccount : API.Account, IStreamListener {
var notification = new API.Notification (node.get_object ());
cached_notifications.add (notification);
});
on_notification.connect (show_notification);
}
~InstanceAccount () {
unsubscribe ();
@ -119,7 +121,7 @@ public class Tootle.InstanceAccount : API.Account, IStreamListener {
return builder.get_root ();
}
public override void on_notification (API.Notification obj) {
protected void show_notification (API.Notification obj) {
var title = Html.remove_tags (obj.kind.get_desc (obj.account));
var notification = new GLib.Notification (title);
if (obj.status != null) {
@ -139,12 +141,7 @@ public class Tootle.InstanceAccount : API.Account, IStreamListener {
}
}
// public override void on_status_removed (int64 id) {
// if (is_current ())
// streams.force_delete (id);
// }
// public override void on_status_added (API.Status status) {
// protected void on_status_added (API.Status status) { //TODO: Watchlist
// if (!is_current ())
// return;

View File

@ -1,11 +1,7 @@
public interface Tootle.IStreamListener : GLib.Object {
public virtual void on_status_removed (int64 id) {}
public virtual void on_status_added (API.Status s) {}
public virtual void on_notification (API.Notification n) {}
public virtual bool accepts (ref string event) {
return true;
}
public signal void on_status_removed (int64 id);
public signal void on_status_added (API.Status s);
public signal void on_notification (API.Notification n);
}

View File

@ -144,24 +144,21 @@ public class Tootle.Streams : Object {
case "update":
var obj = new API.Status (sanitize (root));
c.subscribers.@foreach (s => {
if (s.accepts (ref e))
s.on_status_added (obj);
s.on_status_added (obj);
return true;
});
break;
case "delete":
var id = int64.parse (root.get_string_member ("payload"));
c.subscribers.@foreach (s => {
if (s.accepts (ref e))
s.on_status_removed (id);
s.on_status_removed (id);
return true;
});
break;
case "notification":
var obj = new API.Notification (sanitize (root));
c.subscribers.@foreach (s => {
if (s.accepts (ref e))
s.on_notification (obj);
s.on_notification (obj);
return true;
});
break;

View File

@ -1,24 +1,17 @@
using Gtk;
using Gdk;
public class Tootle.Views.Notifications : Views.Base, IAccountListener, IStreamListener { //TODO: make this a timeline
public class Tootle.Views.Notifications : Views.Timeline, IAccountListener, IStreamListener {
protected InstanceAccount? account = null;
protected int64 last_id = 0;
protected bool force_dot = false;
protected string? stream;
public Notifications () {
app.refresh.connect (on_refresh);
status_button.clicked.connect (on_refresh);
connect_account ();
}
~Notifications () {
streams.unsubscribe (stream, this);
Object ();
on_notification.connect (add_notification);
}
private bool has_unread () {
public bool has_unread () {
if (account == null)
return false;
return last_id > account.last_seen_notification || force_dot;
@ -35,35 +28,21 @@ public class Tootle.Views.Notifications : Views.Base, IAccountListener, IStreamL
return _("Notifications");
}
public void prepend (API.Notification notification) {
append (notification, true);
public override string? get_stream_url () {
return account != null ? @"$(account.instance)/api/v1/streaming/?stream=user&access_token=$(account.token)" : null;
}
public void append (API.Notification notification, bool reverse = false) {
GLib.Idle.add (() => {
var widget = new Widgets.Notification (notification);
content.pack_start (widget, false, false, 0);
public override string get_url () {
if (page_next != null)
return page_next;
if (reverse) {
content.reorder_child (widget, 0);
return "/api/v1/notifications";
}
if (!current) {
force_dot = true;
accounts.active.has_unread_notifications = force_dot;
}
}
on_content_changed ();
if (notification.id > last_id)
last_id = notification.id;
if (has_unread ()) {
accounts.save ();
image.icon_name = get_icon ();
}
return GLib.Source.REMOVE;
});
public override void on_content_changed () {
base.on_content_changed ();
if (image != null && empty)
image.icon_name = get_icon ();
}
public override void on_set_current () {
@ -76,20 +55,43 @@ public class Tootle.Views.Notifications : Views.Base, IAccountListener, IStreamL
}
}
public override void on_content_changed () {
base.on_content_changed ();
if (image != null && empty)
public override void append (Widget? w, bool reverse = false) {
base.append (w, reverse);
var nw = w as Widgets.Notification;
var notification = nw.notification;
if (reverse && !current) {
force_dot = true;
accounts.active.has_unread_notifications = force_dot;
}
if (notification.id > last_id)
last_id = notification.id;
if (has_unread ()) {
accounts.save ();
image.icon_name = get_icon ();
}
}
public virtual void on_refresh () {
clear ();
GLib.Idle.add (request);
public override GLib.Object? to_entity (Json.Object? json) {
if (json != null)
return new API.Notification (json);
else
return null;
}
public virtual void on_account_changed (InstanceAccount? acc) {
account = acc;
streams.unsubscribe (stream, this);
public override Widget? widgetize (GLib.Object? entity) {
var n = entity as API.Notification;
if (n == null)
return null;
var w = new Widgets.Notification (n);
return w;
}
public override void on_account_changed (InstanceAccount? acc) {
base.on_account_changed (acc);
if (account == null) {
last_id = 0;
force_dot = false;
@ -97,51 +99,21 @@ public class Tootle.Views.Notifications : Views.Base, IAccountListener, IStreamL
else {
last_id = account.last_seen_notification;
force_dot = account.has_unread_notifications;
streams.subscribe (get_stream_url (), this, out stream);
}
on_refresh ();
}
public virtual string? get_stream_url () {
return account != null ? @"$(account.instance)/api/v1/streaming/?stream=user&access_token=$(account.token)" : null;
}
public override bool accepts (ref string event) {
return true;
}
public override void on_notification (API.Notification n) {
prepend (n);
}
public bool request () {
public override bool request () {
if (account != null) {
account.cached_notifications.@foreach (notification => {
append (notification);
account.cached_notifications.@foreach (n => {
append (widgetize (n));
return true;
});
}
return base.request ();
}
// new Request.GET ("/api/v1/follow_requests") //TODO: this
// .with_account ()
// .then_parse_array (node => {
// var notification = API.Notification.parse_follow_request (node.get_object ());
// append (notification);
// })
// .on_error (on_error)
// .exec ();
new Request.GET ("/api/v1/notifications")
.with_account (account)
.with_param ("limit", "30")
.then_parse_array (node => {
var notification = new API.Notification (node.get_object ());
append (notification);
})
.on_error (on_error)
.exec ();
return GLib.Source.REMOVE;
protected virtual void add_notification (API.Notification n) {
prepend (widgetize (n));
}
}

View File

@ -154,28 +154,13 @@ public class Tootle.Views.Profile : Views.Timeline {
return base.append_params (req);
}
public override bool request () {
append_params (new Request.GET (get_url ()))
.with_account (account)
.then_parse_array ((node, msg) => {
var obj = node.get_object ();
if (obj != null) {
API.Status status;
if (posts_tab.active)
status = new API.Status (obj);
else {
var account = new API.Account (obj);
status = new API.Status.from_account (account);
}
append (status);
}
get_pages (msg.response_headers.get_one ("Link"));
})
.on_error (on_error)
.exec ();
return GLib.Source.REMOVE;
public override GLib.Object? to_entity (Json.Object? json) {
if (posts_tab.active)
return new API.Status (json);
else {
var account = new API.Account (json);
return new API.Status.from_account (account);
}
}
public static void open_from_id (int64 id){

View File

@ -1,7 +1,7 @@
using Gtk;
using Gdk;
public class Tootle.Views.Timeline : Views.Base, IAccountListener, IStreamListener {
public class Tootle.Views.Timeline : IAccountListener, IStreamListener, Views.Base {
public string timeline { get; construct set; }
public bool is_public { get; construct set; default = false; }
@ -17,6 +17,9 @@ public class Tootle.Views.Timeline : Views.Base, IAccountListener, IStreamListen
app.refresh.connect (on_refresh);
status_button.clicked.connect (on_refresh);
connect_account ();
on_status_added.connect (add_status);
on_status_removed.connect (remove_status);
}
~Timeline () {
streams.unsubscribe (stream, this);
@ -30,32 +33,39 @@ public class Tootle.Views.Timeline : Views.Base, IAccountListener, IStreamListen
return _("Home");
}
public override void on_status_added (API.Status status) {
prepend (status);
}
public virtual bool is_status_owned (API.Status status) {
return status.is_owned ();
}
public void prepend (API.Status status) {
append (status, true);
public virtual GLib.Object? to_entity (Json.Object? json) {
return new API.Status (json);
}
public void append (API.Status status, bool first = false) {
GLib.Idle.add (() => {
var w = new Widgets.Status (status);
w.button_press_event.connect (w.open);
if (!is_status_owned (status))
w.avatar.button_press_event.connect (w.on_avatar_clicked);
public virtual Widget? widgetize (GLib.Object? entity) {
var status = entity as API.Status;
if (status == null)
return null;
content.pack_start (w, false, false, 0);
if (first || status.pinned)
content.reorder_child (w, 0);
var w = new Widgets.Status (status);
w.button_press_event.connect (w.open);
if (!is_status_owned (status))
w.avatar.button_press_event.connect (w.on_avatar_clicked);
on_content_changed ();
return GLib.Source.REMOVE;
});
return w;
}
public void prepend (Widget? w) {
append (w, true);
}
public virtual void append (Widget? w, bool first = false) {
if (w == null)
return;
content.pack_start (w, false, false, 0);
if (first)
content.reorder_child (w, 0);
on_content_changed ();
}
public override void clear () {
@ -101,16 +111,20 @@ public class Tootle.Views.Timeline : Views.Base, IAccountListener, IStreamListen
append_params (new Request.GET (get_url ()))
.with_account (account)
.then_parse_array ((node, msg) => {
var obj = node.get_object ();
if (obj != null) {
var status = new API.Status (obj);
append (status);
}
var obj = node.get_object ();
if (obj == null)
warning ("Received invalid Json.Object");
else {
var entity = to_entity (obj);
if (entity == null)
warning ("Can't convert Json.Object to required entity");
else
append (widgetize (entity));
}
get_pages (msg.response_headers.get_one ("Link"));
})
.on_error (on_error)
.exec ();
return GLib.Source.REMOVE;
}
@ -125,7 +139,7 @@ public class Tootle.Views.Timeline : Views.Base, IAccountListener, IStreamListen
return null;
}
public override void on_account_changed (InstanceAccount? acc) {
public virtual void on_account_changed (InstanceAccount? acc) {
account = acc;
streams.unsubscribe (stream, this);
streams.subscribe (get_stream_url (), this, out stream);
@ -140,18 +154,14 @@ public class Tootle.Views.Timeline : Views.Base, IAccountListener, IStreamListen
request ();
}
public override bool accepts (ref string event) {
var allowed_public = true;
if (is_public)
allowed_public = settings.live_updates_public;
return settings.live_updates && allowed_public;
protected virtual void add_status (API.Status status) {
prepend (widgetize (status));
}
public override void on_status_removed (int64 id) {
protected virtual void remove_status (int64 id) {
content.get_children ().@foreach (w => {
var sw = w as Widgets.Status;
if (sw.status.id == id)
if (sw != null && sw.status.id == id)
sw.destroy ();
});
}