Skip to content

Commit

Permalink
feat(PreviewCard): BookWyrm support (#255)
Browse files Browse the repository at this point in the history
  • Loading branch information
GeopJr committed May 16, 2023
1 parent d4aade6 commit 55bbde0
Show file tree
Hide file tree
Showing 12 changed files with 257 additions and 2 deletions.
3 changes: 3 additions & 0 deletions data/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -317,3 +317,6 @@ GtkSourceAssistant.completion list row{
border-radius: 6px 6px 0px 0px;
}

.bkwm-desc {
padding: 12px;
}
20 changes: 20 additions & 0 deletions src/API/BookWyrm.vala
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
public class Tuba.API.BookWyrm : Entity, Widgetizable {
public string id { get; set; default=""; }
public string openlibraryKey { get; set; default=""; }
public string title { get; set; default=""; }
public string description { get; set; default=""; }
public string isbn13 { get; set; default=""; }
public string publishedDate { get; set; default=""; }
public API.BookWyrmCover? cover { get; set; default=null; }
public Gee.ArrayList<string>? authors { get; set; default=null; }

public static BookWyrm from (Json.Node node) throws Error {
return Entity.from_json (typeof (API.BookWyrm), node) as API.BookWyrm;
}

public override Gtk.Widget to_widget () {
return new Widgets.BookWyrmPage (this);
}

public override void open () {}
}
9 changes: 9 additions & 0 deletions src/API/BookWyrm/Author.vala
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
public class Tuba.API.BookWyrmAuthor : Entity {
public string id { get; set; default=""; }
public string openlibraryKey { get; set; default=""; }
public string name { get; set; default=""; }

public static BookWyrmAuthor from (Json.Node node) throws Error {
return Entity.from_json (typeof (API.BookWyrmAuthor), node) as API.BookWyrmAuthor;
}
}
4 changes: 4 additions & 0 deletions src/API/BookWyrm/Cover.vala
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
public class Tuba.API.BookWyrmCover : Entity {
public string url { get; set; default=""; }
public string name { get; set; default=""; }
}
4 changes: 4 additions & 0 deletions src/API/BookWyrm/meson.build
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
sources += files(
'Author.vala',
'Cover.vala'
)
1 change: 1 addition & 0 deletions src/API/Entity.vala
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ public class Tuba.Entity : GLib.Object, Widgetizable, Json.Serializable {
switch (prop) {
case "supported-mime-types":
case "languages":
case "authors":
return des_list_string(out val, node);
case "media-attachments":
contains = typeof (API.Attachment);
Expand Down
16 changes: 15 additions & 1 deletion src/API/Status/PreviewCard.vala
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,17 @@ public class Tuba.API.PreviewCard : Entity {
public enum CardSpecialType {
BASIC,
PEERTUBE,
FUNKWHALE;
FUNKWHALE,
BOOKWYRM;

public string to_string () {
switch (this) {
case PEERTUBE:
return "PeerTube";
case FUNKWHALE:
return "Funkwhale";
case BOOKWYRM:
return "BookWyrm";
default:
return "";
}
Expand All @@ -31,6 +34,8 @@ public class Tuba.API.PreviewCard : Entity {
return CardSpecialType.PEERTUBE;
} else if (is_funkwhale) {
return CardSpecialType.FUNKWHALE;
} else if (is_bookwyrm) {
return CardSpecialType.BOOKWYRM;
}

return CardSpecialType.BASIC;
Expand Down Expand Up @@ -59,4 +64,13 @@ public class Tuba.API.PreviewCard : Entity {
return kind == "video" && provider_fw && url_fw;
}
}

public bool is_bookwyrm {
get {
bool title_bw = title.last_index_of ("- BookWyrm") > 0;
bool url_bw = url.last_index_of ("/book/") > -1;

return kind == "link" && title_bw && url_bw;
}
}
}
2 changes: 2 additions & 0 deletions src/API/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ sources += files(
'Account.vala',
'AccountField.vala',
'Attachment.vala',
'BookWyrm.vala',
'Conversation.vala',
'Emoji.vala',
'EmojiReaction.vala',
Expand All @@ -23,6 +24,7 @@ sources += files(
)

subdir('Account')
subdir('BookWyrm')
subdir('Funkwhale')
subdir('Instance')
subdir('PeerTube')
Expand Down
39 changes: 39 additions & 0 deletions src/Dialogs/MainWindow.vala
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,45 @@ public class Tuba.Dialogs.MainWindow: Adw.ApplicationWindow, Saveable {
main_stack.visible_child_name = "main";
}

public void show_book (API.BookWyrm book, string? fallback = null) {
try {
var book_widget = book.to_widget ();
var clamp = new Adw.Clamp () {
child = book_widget,
tightening_threshold = 100,
valign = Align.START
};
var scroller = new Gtk.ScrolledWindow () {
hexpand = true,
vexpand = true
};
scroller.child = clamp;

var box = new Gtk.Box(Gtk.Orientation.VERTICAL, 0);
var headerbar = new Adw.HeaderBar() {
css_classes = { "flat" }
};

box.append(headerbar);
box.append(scroller);

var book_dialog = new Adw.Window() {
modal = true,
title = book.title,
transient_for = this,
content = box,
default_width = 360,
default_height = 500
};

book_dialog.show ();

((Widgets.BookWyrmPage) book_widget).selectable = true;
} catch {
if (fallback != null) Host.open_uri (fallback);
}
}

public Views.Base open_view (Views.Base view) {
if ((leaflet.visible_child == view) || (last_view != null && last_view.label == view.label && !view.is_profile)) return view;

Expand Down
137 changes: 137 additions & 0 deletions src/Widgets/BookWyrmPage.vala
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
public class Tuba.Widgets.BookWyrmPage : Gtk.Box {
public API.BookWyrm book { get; private set; }
private Gtk.Picture cover;

// setting labels as selectable before they
// are visible, causes them to be already
// selected when they become visible
private Gtk.Label [] selectable_labels = {};
public bool selectable {
set {
foreach (var label in selectable_labels) {
label.selectable = value;
}
}
}

construct {
orientation = Gtk.Orientation.VERTICAL;
spacing = 12;
margin_bottom = 12;
cover = new Gtk.Picture () {
height_request = 200,
css_classes = { "attachment-picture" }
};

#if GTK_4_8
cover.set_property ("content-fit", 2);
#endif
}
~BookWyrmPage () {
message ("Destroying BookWyrmPage");
selectable_labels = {};
}

public BookWyrmPage (API.BookWyrm t_obj) {
book = t_obj;

var header_box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 32);
if (t_obj.cover != null && t_obj.cover.url != null && t_obj.cover.url != "") {
header_box.append (cover);
image_cache.request_paintable (t_obj.cover.url, on_cache_response);

if (t_obj.cover.name != "") {
cover.alternative_text = cover.tooltip_text = t_obj.cover.name;
}
}

var title_box = new Gtk.Box (Gtk.Orientation.VERTICAL, 6) {
valign = Gtk.Align.CENTER,
hexpand = true
};
var book_title = new Gtk.Label (t_obj.title) {
wrap = true,
css_classes = { "title-1" },
halign = Gtk.Align.START
};
selectable_labels += book_title;
title_box.append (book_title);

if (t_obj.authors != null && t_obj.authors.size > 0) {
Gtk.Label author_label = null;
foreach (var author in t_obj.authors) {
new Request.GET (@"$author.json")
.then ((sess, msg, in_stream) => {
var parser = Network.get_parser_from_inputstream(in_stream);
var node = network.parse_node (parser);
var author_obj = API.BookWyrmAuthor.from (node);
if (author_obj.id == author) {
if (author_label == null) {
author_label = new Gtk.Label (@"by <a href=\"$(author_obj.id)\">$(author_obj.name)</a>") {
wrap = true,
use_markup = true,
halign = Gtk.Align.START
};
title_box.insert_child_after (author_label, book_title);
} else {
author_label.label += @", <a href=\"$(author_obj.id)\">$(author_obj.name)</a>";
}
}
})
.exec ();
}
}

header_box.append (title_box);
append (header_box);

var btn_box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 12);

var bw_btn = new Gtk.Button.with_label("BookWyrm");
bw_btn.clicked.connect (open_on_bw);
btn_box.append (bw_btn);

if (t_obj.openlibraryKey != "") {
var ol_btn = new Gtk.Button.with_label("OpenLibrary");
ol_btn.clicked.connect (open_on_openlibrary);
btn_box.append (ol_btn);
}

append (btn_box);

if (t_obj.description != "") {
var description_label = new Gtk.Label (HtmlUtils.remove_tags (t_obj.description)) {
wrap = true,
css_classes = { "card", "bkwm-desc" }
};
selectable_labels += description_label;
append (description_label);
}

if (t_obj.isbn13 != "") {
var isbn_label = new Gtk.Label (@"ISBN: $(t_obj.isbn13)") {
wrap=true
};
selectable_labels += isbn_label;
append (isbn_label);
}

if (t_obj.publishedDate != "") {
var date_parsed = new GLib.DateTime.from_iso8601 (t_obj.publishedDate, null);
if (date_parsed != null)
append (new Gtk.Label (@"Published: $(date_parsed.format("%x"))") { wrap=true });
}
}

void on_cache_response (bool is_loaded, owned Gdk.Paintable? data) {
cover.paintable = data;
}

void open_on_openlibrary () {
Host.open_uri (@"https://openlibrary.org/books/$(book.openlibraryKey)");
}

void open_on_bw () {
Host.open_uri (book.id);
}
}
23 changes: 22 additions & 1 deletion src/Widgets/Status.vala
Original file line number Diff line number Diff line change
Expand Up @@ -662,6 +662,10 @@ public class Tuba.Widgets.Status : ListBoxRow {
// track as in song
dlg_title = _(@"You are about to open a $(card_special_type) track");
break;
case API.PreviewCard.CardSpecialType.BOOKWYRM:
// translators: the variable is an external service like "BookWyrm"
dlg_title = _(@"You are about to open a $(card_special_type) book");
break;
default:
assert_not_reached ();
}
Expand Down Expand Up @@ -696,6 +700,12 @@ public class Tuba.Widgets.Status : ListBoxRow {
special_host = funkwhale_instance.get_host ();
special_api_url = @"https://$(special_host)/api/v1/tracks/$(Path.get_basename (funkwhale_instance.get_path ()))";
break;
case API.PreviewCard.CardSpecialType.BOOKWYRM:
var bookwyrm_instance = GLib.Uri.parse (status.formal.card.url, GLib.UriFlags.NONE);
special_host = bookwyrm_instance.get_host ();
var bookwyrm_id = Path.get_basename (Path.get_dirname (Path.get_dirname (status.formal.card.url)));
special_api_url = @"https://$(special_host)/book/$(bookwyrm_id).json";
break;
default:
assert_not_reached ();
}
Expand All @@ -706,6 +716,7 @@ public class Tuba.Widgets.Status : ListBoxRow {
var parser = Network.get_parser_from_inputstream(in_stream);
var node = network.parse_node (parser);
var res_url = "";
API.BookWyrm? bookwyrm_obj = null;

switch (card_special_type) {
case API.PreviewCard.CardSpecialType.PEERTUBE:
Expand Down Expand Up @@ -740,14 +751,24 @@ public class Tuba.Widgets.Status : ListBoxRow {
}
}
break;
case API.PreviewCard.CardSpecialType.BOOKWYRM:
bookwyrm_obj = API.BookWyrm.from (node);
res_url = bookwyrm_obj.id;

if (bookwyrm_obj.title != null && bookwyrm_obj.title != "") failed = false;
break;
default:
assert_not_reached ();
}

if (failed || res_url == "") {
Host.open_uri (status.formal.card.url);
} else {
app.main_window.show_media_viewer_remote_video (res_url, null, status.formal.card.url);
if (bookwyrm_obj == null) {
app.main_window.show_media_viewer_remote_video (res_url, null, status.formal.card.url);
} else {
app.main_window.show_book (bookwyrm_obj, status.formal.card.url);
}
}
})
.on_error (() => {
Expand Down
1 change: 1 addition & 0 deletions src/Widgets/meson.build
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
sources += files(
'Avatar.vala',
'Background.vala',
'BookWyrmPage.vala',
'Conversation.vala',
'Emoji.vala',
'EmojiLabel.vala',
Expand Down

0 comments on commit 55bbde0

Please sign in to comment.