diff --git a/data/com.github.bleakgrey.tootle.gresource.xml b/data/com.github.bleakgrey.tootle.gresource.xml index 0dd6a38..11eb9e2 100644 --- a/data/com.github.bleakgrey.tootle.gresource.xml +++ b/data/com.github.bleakgrey.tootle.gresource.xml @@ -2,6 +2,7 @@ Application.css - logo128.png + logo128.png + empty_state.png diff --git a/data/empty_state.png b/data/empty_state.png new file mode 100644 index 0000000..4a0cfcc Binary files /dev/null and b/data/empty_state.png differ diff --git a/src/Views/AbstractView.vala b/src/Views/AbstractView.vala index b7d053c..f9d8a12 100644 --- a/src/Views/AbstractView.vala +++ b/src/Views/AbstractView.vala @@ -2,8 +2,9 @@ using Gtk; public abstract class Tootle.AbstractView : Gtk.ScrolledWindow { - public Gtk.Image image; + public Gtk.Image? image; public Gtk.Box view; + protected Gtk.Box? empty; construct { view = new Gtk.Box (Gtk.Orientation.VERTICAL, 0); @@ -40,4 +41,30 @@ public abstract class Tootle.AbstractView : Gtk.ScrolledWindow { public virtual void bottom_reached (){} + public virtual bool is_empty () { + return view.get_children ().length () <= 1; + } + + public virtual bool empty_state () { + if (empty != null) + empty.destroy (); + if (!is_empty ()) + return false; + + empty = new Gtk.Box (Gtk.Orientation.VERTICAL, 0); + empty.margin = 64; + var image = new Image.from_resource ("/com/github/bleakgrey/tootle/empty_state"); + var text = new Gtk.Label (_("Nothing to see here")); + text.get_style_context ().add_class ("h2"); + text.opacity = 0.5; + empty.vexpand = true; + empty.valign = Gtk.Align.FILL; + empty.pack_start (image, false, false, 0); + empty.pack_start (text, false, false, 12); + empty.show_all (); + view.pack_start (empty, false, false, 0); + + return true; + } + } diff --git a/src/Views/AccountView.vala b/src/Views/AccountView.vala index 9731a2c..b8ab96a 100644 --- a/src/Views/AccountView.vala +++ b/src/Views/AccountView.vala @@ -197,6 +197,10 @@ public class Tootle.AccountView : TimelineView { return btn; } + public override bool is_empty () { + return view.get_children ().length () <= 2; + } + public override bool is_status_owned (Status status){ return status.get_formal ().account.id == account.id; } diff --git a/src/Views/AddAccountView.vala b/src/Views/AddAccountView.vala index 23557ae..1ec348f 100644 --- a/src/Views/AddAccountView.vala +++ b/src/Views/AddAccountView.vala @@ -18,7 +18,7 @@ public class Tootle.AddAccountView : Tootle.AbstractView { hexpand = true; halign = Gtk.Align.CENTER; - image = new Image.from_resource ("/com/github/bleakgrey/tootle/logo128.png"); + image = new Image.from_resource ("/com/github/bleakgrey/tootle/logo128"); image.halign = Gtk.Align.CENTER; image.hexpand = true; image.margin_bottom = 24; diff --git a/src/Views/FollowersView.vala b/src/Views/FollowersView.vala index 7f2f13f..37c2b54 100644 --- a/src/Views/FollowersView.vala +++ b/src/Views/FollowersView.vala @@ -4,10 +4,12 @@ public class Tootle.FollowersView : TimelineView { public FollowersView (ref Account account) { base (account.id.to_string ()); - } public new void prepend (ref Account account){ + if (empty != null) + empty.destroy (); + var separator = new Gtk.Separator (Gtk.Orientation.HORIZONTAL); separator.show (); @@ -27,7 +29,7 @@ public class Tootle.FollowersView : TimelineView { public override void request (){ var msg = new Soup.Message("GET", get_url ()); - debug (get_url ()); + msg.finished.connect (() => empty_state ()); Tootle.network.queue(msg, (sess, mess) => { try{ Tootle.network.parse_array (mess).foreach_element ((array, i, node) => { diff --git a/src/Views/NotificationsView.vala b/src/Views/NotificationsView.vala index aebeb57..c668850 100644 --- a/src/Views/NotificationsView.vala +++ b/src/Views/NotificationsView.vala @@ -22,7 +22,10 @@ public class Tootle.NotificationsView : AbstractView { return _("Notifications"); } - public void prepend (Notification notification, bool invert_order = false) { + public void prepend (Notification notification) { + if (empty != null) + empty.destroy (); + var separator = new Gtk.Separator (Gtk.Orientation.HORIZONTAL); separator.show (); @@ -37,8 +40,15 @@ public class Tootle.NotificationsView : AbstractView { if (!(widget is NotificationWidget)) return; - if (view.get_children ().length () <= 1) + empty_state (); + } + + public override bool empty_state () { + var is_empty = base.empty_state (); + if (image != null && is_empty) image.icon_name = get_icon (); + + return is_empty; } public virtual void on_refresh () { @@ -89,6 +99,8 @@ public class Tootle.NotificationsView : AbstractView { warning (e.message); } }); + + empty_state (); } } diff --git a/src/Views/SearchView.vala b/src/Views/SearchView.vala index 48013f6..8751cd1 100644 --- a/src/Views/SearchView.vala +++ b/src/Views/SearchView.vala @@ -44,12 +44,6 @@ public class Tootle.SearchView : AbstractView { view.pack_start (widget, false, false, 0); } - private void append_empty_state (){ - var empty_state = new Gtk.Label (_("No Results")); - empty_state.get_style_context ().add_class ("h2"); - view.pack_start (empty_state, false, false, 6); - } - private void append_hashtag (string name) { var text = "#%s".printf (Tootle.settings.instance_url, Soup.URI.encode (name, null), name); var widget = new RichLabel (text); @@ -79,10 +73,6 @@ public class Tootle.SearchView : AbstractView { var hashtags = root.get_array_member ("hashtags"); clear (); - if (accounts.get_length () == 0 && statuses.get_length () == 0 && hashtags.get_length () == 0) { - append_empty_state (); - return; - } if (accounts.get_length () > 0) { append_header (_("Accounts")); @@ -108,6 +98,8 @@ public class Tootle.SearchView : AbstractView { append_hashtag (node.get_string ()); }); } + + empty_state (); } catch (GLib.Error e) { diff --git a/src/Views/TimelineView.vala b/src/Views/TimelineView.vala index 4c5891f..ee4a59e 100644 --- a/src/Views/TimelineView.vala +++ b/src/Views/TimelineView.vala @@ -36,6 +36,9 @@ public class Tootle.TimelineView : AbstractView { } public void prepend (ref Status status){ + if (empty != null) + empty.destroy (); + var separator = new Gtk.Separator (Gtk.Orientation.HORIZONTAL); separator.show (); @@ -58,16 +61,12 @@ public class Tootle.TimelineView : AbstractView { public virtual void on_remove (Widget widget){ if (!(widget is StatusWidget)) return; - - //TODO: empty state } public void get_pages (string? header) { page_next = page_prev = null; - if (header == null) { - debug ("No pagingation links"); + if (header == null) return; - } var pages = header.split (","); foreach (var page in pages) { @@ -76,14 +75,10 @@ public class Tootle.TimelineView : AbstractView { .replace (">", "") .split (";")[0]; - if ("rel=\"prev\"" in page) { - debug ("found prev page %s", sanitized); + if ("rel=\"prev\"" in page) page_prev = sanitized; - } - else { - debug ("found next page %s", sanitized); + else page_next = sanitized; - } } is_last_page = page_prev != null & page_next == null; @@ -100,6 +95,7 @@ public class Tootle.TimelineView : AbstractView { public virtual void request (){ var msg = new Soup.Message("GET", get_url ()); + msg.finished.connect (() => empty_state ()); Tootle.network.queue(msg, (sess, mess) => { try{ Tootle.network.parse_array (mess).foreach_element ((array, i, node) => { @@ -126,7 +122,6 @@ public class Tootle.TimelineView : AbstractView { public virtual void on_account_changed (Account? account){ if(account == null) return; - on_refresh (); }