Rewrite pagination, implement followers view
This commit is contained in:
parent
72cddb0541
commit
349a05bbd5
|
@ -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: [
|
||||
|
|
|
@ -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 ());
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 ();
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
using Gtk;
|
||||
|
||||
public class Tootle.FederatedView : Tootle.HomeView {
|
||||
public class Tootle.FederatedView : TimelineView {
|
||||
|
||||
public FederatedView () {
|
||||
base ("public");
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
using Gtk;
|
||||
|
||||
public class Tootle.LocalView : Tootle.HomeView {
|
||||
public class Tootle.LocalView : TimelineView {
|
||||
|
||||
public LocalView () {
|
||||
base ("public");
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
using Gtk;
|
||||
using Gdk;
|
||||
|
||||
public class Tootle.NotificationsView : Tootle.AbstractView {
|
||||
public class Tootle.NotificationsView : AbstractView {
|
||||
|
||||
public NotificationsView () {
|
||||
base ();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 ();
|
||||
}
|
||||
|
|
@ -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;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue