Introduce Widgetizable interface

This commit is contained in:
Bleak Grey 2020-06-03 18:06:11 +03:00
parent d24e10ace2
commit 82e2f93bc8
12 changed files with 71 additions and 87 deletions

View File

@ -58,6 +58,8 @@ executable(
'src/API/Notification.vala',
'src/API/NotificationType.vala',
'src/API/Attachment.vala',
'src/API/Conversation.vala',
'src/Widgets/Widgetizable.vala',
'src/Widgets/Avatar.vala',
'src/Widgets/AccountsButton.vala',
'src/Widgets/RichLabel.vala',

View File

@ -0,0 +1,10 @@
public class Tootle.API.Conversation : GLib.Object, Json.Serializable, Widgetizable {
public string id { get; construct set; }
public bool unread { get; set; default = false; }
public Conversation () {
GLib.Object ();
}
}

View File

@ -1,4 +1,4 @@
public class Tootle.API.Notification : GLib.Object {
public class Tootle.API.Notification : GLib.Object, Widgetizable {
public int64 id { get; construct set; }
public Account account { get; construct set; }
@ -27,6 +27,10 @@ public class Tootle.API.Notification : GLib.Object {
);
}
public override Gtk.Widget to_widget () {
return new Widgets.Notification (this);
}
public Json.Node? serialize () {
var builder = new Json.Builder ();
builder.begin_object ();

View File

@ -1,7 +1,6 @@
using Gee;
public class Tootle.API.Status : GLib.Object {
public class Tootle.API.Status : GLib.Object, Widgetizable {
public int64 id { get; construct set; } //TODO: IDs are no longer guaranteed to be numbers. Replace with strings.
public API.Account account { get; construct set; }
@ -115,6 +114,13 @@ public class Tootle.API.Status : GLib.Object {
content = Html.remove_tags (account.note);
}
public override Gtk.Widget to_widget () {
var w = new Widgets.Status (this);
w.button_press_event.connect (w.open);
return w;
}
public Json.Node? serialize () {
var builder = new Json.Builder ();
builder.begin_object ();

View File

@ -6,7 +6,8 @@ namespace Tootle {
public errordomain Oopsie {
USER,
PARSING,
INSTANCE
INSTANCE,
INTERNAL
}
public static Application app;

View File

@ -4,7 +4,8 @@ public class Tootle.Views.Conversations : Views.Timeline {
Object (
url: "/api/v1/conversations",
label: _("Conversations"),
icon: "mail-send-symbolic"
icon: API.Visibility.DIRECT.get_icon (),
accepts: typeof (API.Conversation)
);
}

View File

@ -11,6 +11,7 @@ public class Tootle.Views.Notifications : Views.Timeline, IAccountListener, IStr
label: _("Notifications"),
icon: Desktop.fallback_icon ("notification-symbolic", "preferences-system-notifications-symbolic", "user-invisible-symbolic")
);
accepts = typeof (API.Notification);
on_notification.connect (add_notification);
on_status_added.disconnect (add_status);
}
@ -41,20 +42,15 @@ public class Tootle.Views.Notifications : Views.Timeline, IAccountListener, IStr
accounts.save ();
}
public override GLib.Object? to_entity (Json.Object? json) {
if (json != null)
return new API.Notification (json);
else
return null;
}
public override GLib.Object to_entity (Json.Node node) throws Oopsie {
if (node == null)
throw new Oopsie.PARSING ("Received null Json.Node");
public override Widget? widgetize (GLib.Object? entity) {
var n = entity as API.Notification;
if (n == null)
return null;
var obj = node.get_object ();
if (obj == null)
throw new Oopsie.PARSING ("Received Json.Node is not a Json.Object!");
var w = new Widgets.Notification (n);
return w;
return new API.Notification (obj);
}
public override void on_account_changed (InstanceAccount? acc) {
@ -72,7 +68,7 @@ public class Tootle.Views.Notifications : Views.Timeline, IAccountListener, IStr
public override bool request () {
if (account != null) {
account.cached_notifications.@foreach (n => {
append (widgetize (n));
append (n.to_widget ());
return true;
});
}
@ -86,7 +82,7 @@ public class Tootle.Views.Notifications : Views.Timeline, IAccountListener, IStr
}
void add_notification (API.Notification n) {
prepend (widgetize (n));
prepend (n.to_widget ());
}
}

View File

@ -156,11 +156,12 @@ public class Tootle.Views.Profile : Views.Timeline {
return base.append_params (req);
}
public override GLib.Object? to_entity (Json.Object? json) {
public override GLib.Object to_entity (Json.Node node) {
var obj = node.get_object ();
if (posts_tab.active)
return new API.Status (json);
return new API.Status (obj);
else {
var account = new API.Account (json);
var account = new API.Account (obj);
return new API.Status.from_account (account);
}
}

View File

@ -5,6 +5,7 @@ public class Tootle.Views.Timeline : IAccountListener, IStreamListener, Views.Ba
public string url { get; construct set; }
public bool is_public { get; construct set; default = false; }
public Type accepts { get; set; default = typeof (API.Status); }
protected InstanceAccount? account = null;
@ -29,21 +30,15 @@ public class Tootle.Views.Timeline : IAccountListener, IStreamListener, Views.Ba
return status.is_owned ();
}
public virtual GLib.Object? to_entity (Json.Object? json) {
return new API.Status (json);
}
public virtual GLib.Object to_entity (Json.Node node) throws Oopsie {
if (node == null)
throw new Oopsie.PARSING ("Received null Json.Node");
public virtual Widget? widgetize (GLib.Object? entity) {
var status = entity as API.Status;
if (status == null)
return null;
var obj = node.get_object ();
if (obj == null)
throw new Oopsie.PARSING ("Received Json.Node is not a Json.Object!");
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);
return w;
return new API.Status (obj);
}
public void prepend (Widget? w) {
@ -51,8 +46,10 @@ public class Tootle.Views.Timeline : IAccountListener, IStreamListener, Views.Ba
}
public virtual void append (Widget? w, bool first = false) {
if (w == null)
if (w == null) {
warning ("Attempted to add an empty widget");
return;
}
if (first)
content_list.prepend (w);
@ -105,15 +102,13 @@ public class Tootle.Views.Timeline : IAccountListener, IStreamListener, Views.Ba
append_params (new Request.GET (get_req_url ()))
.with_account (account)
.then_parse_array ((node, msg) => {
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));
try {
var e = to_entity (node);
var w = e as Widgetizable;
append (w.to_widget ());
}
catch (Error e) {
warning (@"Timeline item parse error: $(e.message)");
}
get_pages (msg.response_headers.get_one ("Link"));
})
@ -154,7 +149,7 @@ public class Tootle.Views.Timeline : IAccountListener, IStreamListener, Views.Ba
allow_update = settings.public_live_updates;
if (settings.live_updates && allow_update)
prepend (widgetize (status));
prepend (status.to_widget ());
}
protected virtual void remove_status (int64 id) {

View File

@ -1,40 +0,0 @@
using Gdk;
public class Tootle.Widgets.Account : Widgets.Status {
public Account (API.Account account) {
var status = new API.Status (-1);
status.account = account;
//status.url = account.url;
//status.content = "<a href=\"%s\">@%s</a>".printf (account.url, account.acct);
//status.created_at = account.created_at;
base (status);
//counters.visible = false;
//title_acct.visible = false;
//content_label.margin_bottom = 12;
}
protected override bool on_clicked (EventButton ev) {
if (ev.button == 1)
return on_avatar_clicked (ev);
return false;
}
public override bool open_menu (uint button, uint32 time) {
var menu = new Gtk.Menu ();
var item_open_link = new Gtk.MenuItem.with_label (_("Open in Browser"));
item_open_link.activate.connect (() => Desktop.open_uri (status.url));
var item_copy_link = new Gtk.MenuItem.with_label (_("Copy Link"));
item_copy_link.activate.connect (() => Desktop.copy (status.url));
menu.add (item_open_link);
menu.add (item_copy_link);
menu.show_all ();
menu.popup_at_pointer ();
return true;
}
}

View File

@ -135,6 +135,7 @@ public class Tootle.Widgets.Status : EventBox {
}
menu_button.clicked.connect (open_menu);
avatar.button_press_event.connect (on_avatar_clicked);
}
public Status (API.Status status, API.NotificationType? _kind = null) {
@ -166,7 +167,7 @@ public class Tootle.Widgets.Status : EventBox {
var view = new Views.Profile (status.formal.account);
return window.open_view (view);
}
return false;
return true;
}
public bool open (EventButton ev) {

View File

@ -0,0 +1,7 @@
public interface Tootle.Widgetizable : GLib.Object {
public virtual Gtk.Widget to_widget () throws Oopsie {
throw new Tootle.Oopsie.INTERNAL ("Widgetizable didn't provide a Widget!");
}
}