Rewrite pagination, implement followers view

This commit is contained in:
bleakgrey 2018-05-19 15:21:03 +03:00
parent 72cddb0541
commit 349a05bbd5
16 changed files with 175 additions and 45 deletions

View File

@ -40,18 +40,21 @@ executable(
'src/Widgets/ImageToggleButton.vala',
'src/Widgets/AccountsButton.vala',
'src/Widgets/StatusWidget.vala',
'src/Widgets/AccountWidget.vala',
'src/Widgets/NotificationWidget.vala',
'src/Widgets/AttachmentWidget.vala',
'src/Widgets/AttachmentBox.vala',
'src/Dialogs/PostDialog.vala',
'src/Views/AbstractView.vala',
'src/Views/AddAccountView.vala',
'src/Views/HomeView.vala',
'src/Views/TimelineView.vala',
'src/Views/LocalView.vala',
'src/Views/FederatedView.vala',
'src/Views/NotificationsView.vala',
'src/Views/StatusView.vala',
'src/Views/AccountView.vala',
'src/Views/FollowersView.vala',
'src/Views/FollowingView.vala',
'src/Views/FavoritesView.vala',
'src/Views/SearchView.vala',
dependencies: [

View File

@ -76,7 +76,7 @@ public class Tootle.MainWindow: Gtk.Window {
}
private void build_main_view (){
add_header_view (new HomeView ());
add_header_view (new TimelineView ("home"));
add_header_view (new NotificationsView ());
add_header_view (new LocalView ());
add_header_view (new FederatedView ());

View File

@ -124,7 +124,7 @@ public class Tootle.NetManager : GLib.Object {
public void load_avatar (string url, Granite.Widgets.Avatar avatar, int size = 32){
var msg = new Soup.Message("GET", url);
msg.finished.connect(() => {
var data = msg.response_body.flatten ().data;
var data = msg.response_body.data;
var stream = new MemoryInputStream.from_data (data);
var pixbuf = new Gdk.Pixbuf.from_stream_at_scale (stream, size, size, true);
avatar.pixbuf = pixbuf;
@ -141,7 +141,8 @@ public class Tootle.NetManager : GLib.Object {
// image.set_from_pixbuf (pixbuf);
var loader = new PixbufLoader ();
loader.write (msg.response_body.data);
var data = msg.response_body.data;
loader.write (data);
loader.close ();
var pixbuf = loader.get_pixbuf ();
image.set_from_pixbuf (pixbuf);
@ -152,7 +153,7 @@ public class Tootle.NetManager : GLib.Object {
public void load_scaled_image (string url, Gtk.Image image, int size = 64) {
var msg = new Soup.Message("GET", url);
msg.finished.connect(() => {
var data = msg.response_body.flatten ().data;
var data = msg.response_body.data;
var stream = new MemoryInputStream.from_data (data);
var pixbuf = new Gdk.Pixbuf.from_stream_at_scale (stream, size, size, true);
image.set_from_pixbuf (pixbuf);

View File

@ -1,8 +1,7 @@
using Gtk;
public abstract class Tootle.AbstractView : Gtk.ScrolledWindow {
public int64 max_id = -1;
public Gtk.Image image;
public Gtk.Box view;
@ -33,9 +32,7 @@ public abstract class Tootle.AbstractView : Gtk.ScrolledWindow {
}
public virtual void clear (){
max_id = -1;
view.forall (widget => widget.destroy ());
pre_construct ();
}

View File

@ -1,7 +1,7 @@
using Gtk;
using Granite;
public class Tootle.AccountView : Tootle.HomeView {
public class Tootle.AccountView : TimelineView {
Account account;
@ -107,12 +107,19 @@ public class Tootle.AccountView : Tootle.HomeView {
}
public AccountView (Account acc) {
base ("");
account = acc;
account.updated.connect(rebind);
add_counter (_("TOOTS"), 1, account.statuses_count);
add_counter (_("FOLLOWS"), 2, account.following_count);
add_counter (_("FOLLOWERS"), 3, account.followers_count);
add_counter (_("FOLLOWS"), 2, account.following_count).clicked.connect (() => {
var view = new FollowingView (ref account);
Tootle.window.open_view (view);
});
add_counter (_("FOLLOWERS"), 3, account.followers_count).clicked.connect (() => {
var view = new FollowersView (ref account);
Tootle.window.open_view (view);
});
show_all ();
@ -195,11 +202,10 @@ public class Tootle.AccountView : Tootle.HomeView {
}
public override string get_url () {
var url = "%s/api/v1/accounts/%lld/statuses".printf (Tootle.settings.instance_url, account.id);
url += "?limit=25";
if (page_next != null)
return page_next;
if (max_id > 0)
url += "&max_id=" + max_id.to_string ();
var url = "%s/api/v1/accounts/%lld/statuses?limit=%i".printf (Tootle.settings.instance_url, account.id, this.limit);
return url;
}

View File

@ -1,6 +1,6 @@
using Gtk;
public class Tootle.FavoritesView : Tootle.HomeView {
public class Tootle.FavoritesView : TimelineView {
public FavoritesView () {
base ("favorites");
@ -8,11 +8,10 @@ public class Tootle.FavoritesView : Tootle.HomeView {
}
public override string get_url (){
var url = "%s/api/v1/favourites?limit=25".printf (Tootle.settings.instance_url);
if (max_id > 0)
url += "&max_id=" + max_id.to_string ();
if (page_next != null)
return page_next;
var url = "%s/api/v1/favourites/?limit=%i".printf (Tootle.settings.instance_url, this.limit);
return url;
}

View File

@ -1,6 +1,6 @@
using Gtk;
public class Tootle.FederatedView : Tootle.HomeView {
public class Tootle.FederatedView : TimelineView {
public FederatedView () {
base ("public");

View File

@ -0,0 +1,50 @@
using Gtk;
public class Tootle.FollowersView : TimelineView {
public FollowersView (ref Account account) {
base (account.id.to_string ());
}
public new void prepend (ref Account account){
var separator = new Gtk.Separator (Gtk.Orientation.HORIZONTAL);
separator.show ();
var widget = new AccountWidget (ref account);
widget.separator = separator;
view.pack_start(separator, false, false, 0);
view.pack_start(widget, false, false, 0);
}
public override string get_url (){
if (page_next != null)
return page_next;
var url = "%s/api/v1/accounts/%s/followers".printf (Tootle.settings.instance_url, this.timeline);
return url;
}
public override void request (){
var msg = new Soup.Message("GET", get_url ());
debug (get_url ());
Tootle.network.queue(msg, (sess, mess) => {
try{
Tootle.network.parse_array (mess).foreach_element ((array, i, node) => {
var object = node.get_object ();
if (object != null){
var status = Account.parse (object);
prepend (ref status);
}
});
get_pages (mess.response_headers.get_one ("Link"));
}
catch (GLib.Error e) {
warning ("Can't get account follow info:");
warning (e.message);
}
});
}
}

View File

@ -0,0 +1,18 @@
using Gtk;
public class Tootle.FollowingView : FollowersView {
public FollowingView (ref Account account) {
base (ref account);
}
public override string get_url (){
if (page_next != null)
return page_next;
var url = "%s/api/v1/accounts/%s/following".printf (Tootle.settings.instance_url, this.timeline);
return url;
}
}

View File

@ -1,6 +1,6 @@
using Gtk;
public class Tootle.LocalView : Tootle.HomeView {
public class Tootle.LocalView : TimelineView {
public LocalView () {
base ("public");

View File

@ -1,7 +1,7 @@
using Gtk;
using Gdk;
public class Tootle.NotificationsView : Tootle.AbstractView {
public class Tootle.NotificationsView : AbstractView {
public NotificationsView () {
base ();

View File

@ -24,17 +24,7 @@ public class Tootle.SearchView : AbstractView {
}
private void append_account (ref Account acc) {
var _status = new Status (-1);
_status.account = acc;
_status.content = "<a href=\"%s\">@%s</a>".printf (acc.url, acc.acct);
_status.created_at = acc.created_at;
var widget = new StatusWidget (ref _status);
widget.counters.visible = false;
widget.title_acct.visible = false;
widget.button_press_event.connect(() => {
widget.on_avatar_clicked ();
return true;
});
var widget = new AccountWidget (ref acc);
view.pack_start (widget, false, false, 0);
}

View File

@ -1,6 +1,6 @@
using Gtk;
public class Tootle.StatusView : Tootle.AbstractView {
public class Tootle.StatusView : AbstractView {
private Status root_status;
bool last_was_a_root = false;

View File

@ -1,13 +1,20 @@
using Gtk;
using Gdk;
public class Tootle.HomeView : Tootle.AbstractView {
public class Tootle.TimelineView : AbstractView {
private string timeline;
protected string timeline;
protected string pars;
protected int limit = 25;
protected bool is_last_page = false;
protected string? page_next;
protected string? page_prev;
public HomeView (string timeline = "home") {
public TimelineView (string timeline, string pars = "") {
base ();
this.timeline = timeline;
this.pars = pars;
view.remove.connect (on_remove);
Tootle.accounts.switched.connect(on_account_changed);
@ -41,6 +48,13 @@ public class Tootle.HomeView : Tootle.AbstractView {
view.pack_start(widget, false, false, 0);
}
public override void clear () {
this.page_prev = null;
this.page_next = null;
this.is_last_page = false;
base.clear ();
}
public virtual void on_remove (Widget widget){
if (!(widget is StatusWidget))
return;
@ -48,11 +62,39 @@ public class Tootle.HomeView : Tootle.AbstractView {
//TODO: empty state
}
public virtual string get_url () {
var url = "%s/api/v1/timelines/%s?limit=25".printf (Tootle.settings.instance_url, this.timeline);
if (max_id > 0)
url += "&max_id=" + max_id.to_string ();
public void get_pages (string? header) {
page_next = page_prev = null;
if (header == null) {
debug ("No pagingation links");
return;
}
var pages = header.split (",");
foreach (var page in pages) {
var sanitized = page
.replace ("<","")
.replace (">", "")
.split (";")[0];
if ("rel=\"prev\"" in page) {
debug ("found prev page %s", sanitized);
page_prev = sanitized;
}
else {
debug ("found next page %s", sanitized);
page_next = sanitized;
}
}
is_last_page = page_prev != null & page_next == null;
}
public virtual string get_url () {
if (page_next != null)
return page_next;
var url = "%s/api/v1/timelines/%s?limit=%i".printf (Tootle.settings.instance_url, this.timeline, this.limit);
url += this.pars;
return url;
}
@ -64,10 +106,10 @@ public class Tootle.HomeView : Tootle.AbstractView {
var object = node.get_object ();
if (object != null){
var status = Status.parse(object);
max_id = status.id;
prepend (ref status);
}
});
get_pages (mess.response_headers.get_one ("Link"));
}
catch (GLib.Error e) {
warning ("Can't update feed");
@ -89,6 +131,10 @@ public class Tootle.HomeView : Tootle.AbstractView {
}
public override void bottom_reached (){
if (is_last_page) {
debug ("Last page reached");
return;
}
request ();
}

View File

@ -0,0 +1,20 @@
public class Tootle.AccountWidget : StatusWidget {
public AccountWidget (ref Account account) {
var status = new Status (-1);
status.account = account;
status.content = "<a href=\"%s\">@%s</a>".printf (account.url, account.acct);
status.created_at = account.created_at;
base (ref status);
counters.visible = false;
title_acct.visible = false;
content_label.margin_bottom = 12;
button_press_event.connect(() => {
on_avatar_clicked ();
return true;
});
}
}

View File

@ -32,7 +32,7 @@ public class Tootle.RichLabel : Gtk.Label {
if ("/tags/" in url){
var encoded = url.split("/tags/")[1];
var hashtag = Soup.URI.decode (encoded);
var feed = new HomeView ("tag/" + hashtag);
var feed = new TimelineView ("tag/" + hashtag);
Tootle.window.open_view (feed);
return true;
}