mirror of
https://gitlab.gnome.org/World/tootle
synced 2025-02-08 07:38:39 +01:00
Implement attachments
This commit is contained in:
parent
ac16f77555
commit
e0b45ee49c
@ -27,3 +27,8 @@
|
||||
.header-counters{
|
||||
background:rgba(255,255,255,.4);
|
||||
}
|
||||
|
||||
.attachment{
|
||||
background: #fff;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
@ -34,12 +34,14 @@ executable(
|
||||
'src/API/StatusVisibility.vala',
|
||||
'src/API/Notification.vala',
|
||||
'src/API/NotificationType.vala',
|
||||
'src/API/Attachment.vala',
|
||||
'src/Widgets/HeaderBar.vala',
|
||||
'src/Widgets/AlignedLabel.vala',
|
||||
'src/Widgets/RichLabel.vala',
|
||||
'src/Widgets/AccountsButton.vala',
|
||||
'src/Widgets/StatusWidget.vala',
|
||||
'src/Widgets/NotificationWidget.vala',
|
||||
'src/Widgets/AttachmentWidget.vala',
|
||||
'src/Dialogs/PostDialog.vala',
|
||||
'src/Views/AbstractView.vala',
|
||||
'src/Views/AddAccountView.vala',
|
||||
|
23
src/API/Attachment.vala
Normal file
23
src/API/Attachment.vala
Normal file
@ -0,0 +1,23 @@
|
||||
public class Tootle.Attachment{
|
||||
|
||||
public int64 id;
|
||||
public string type;
|
||||
public string url;
|
||||
public string preview_url;
|
||||
|
||||
public Attachment(int64 id){
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public static Attachment parse (Json.Object obj){
|
||||
var id = int64.parse (obj.get_string_member ("id"));
|
||||
var attachment = new Attachment (id);
|
||||
|
||||
attachment.type = obj.get_string_member ("type");
|
||||
attachment.preview_url = obj.get_string_member ("preview_url");
|
||||
attachment.url = obj.get_string_member ("url");
|
||||
|
||||
return attachment;
|
||||
}
|
||||
|
||||
}
|
@ -13,9 +13,11 @@ public class Tootle.Status{
|
||||
public string created_at;
|
||||
public bool reblogged;
|
||||
public bool favorited;
|
||||
public bool sensitive;
|
||||
public Status? reblog;
|
||||
public Mention[]? mentions;
|
||||
public Tag[]? tags;
|
||||
public Attachment[]? attachments;
|
||||
|
||||
public Status(int64 id) {
|
||||
this.id = id;
|
||||
@ -38,24 +40,7 @@ public class Tootle.Status{
|
||||
status.reblogs_count = obj.get_int_member ("reblogs_count");
|
||||
status.favourites_count = obj.get_int_member ("favourites_count");
|
||||
status.content = Utils.escape_html ( obj.get_string_member ("content"));
|
||||
|
||||
Mention[]? _mentions = {};
|
||||
obj.get_array_member ("mentions").foreach_element ((array, i, node) => {
|
||||
var object = node.get_object ();
|
||||
if (object != null)
|
||||
_mentions += Mention.parse (object);
|
||||
});
|
||||
if (_mentions.length > 0)
|
||||
status.mentions = _mentions;
|
||||
|
||||
Tag[]? _tags = {};
|
||||
obj.get_array_member ("tags").foreach_element ((array, i, node) => {
|
||||
var object = node.get_object ();
|
||||
if (object != null)
|
||||
_tags += Tag.parse (object);
|
||||
});
|
||||
if (_tags.length > 0)
|
||||
status.tags = _tags;
|
||||
status.sensitive = obj.get_boolean_member ("sensitive");
|
||||
|
||||
var spoiler = obj.get_string_member ("spoiler_text");
|
||||
if (spoiler != "")
|
||||
@ -69,6 +54,33 @@ public class Tootle.Status{
|
||||
if(obj.has_member ("reblog") && obj.get_null_member("reblog") != true)
|
||||
status.reblog = Status.parse (obj.get_object_member ("reblog"));
|
||||
|
||||
Mention[]? _mentions = {};
|
||||
obj.get_array_member ("mentions").foreach_element ((array, i, node) => {
|
||||
var object = node.get_object ();
|
||||
if (object != null)
|
||||
_mentions += Mention.parse (object);
|
||||
});
|
||||
if (_mentions.length > 0)
|
||||
status.mentions = _mentions;
|
||||
|
||||
Tag[]? _tags = {};
|
||||
obj.get_array_member ("tags").foreach_element ((array, i, node) => {
|
||||
var object = node.get_object ();
|
||||
if (object != null)
|
||||
_tags += Tag.parse (object);
|
||||
});
|
||||
if (_tags.length > 0)
|
||||
status.tags = _tags;
|
||||
|
||||
Attachment[]? _attachments = {};
|
||||
obj.get_array_member ("media_attachments").foreach_element ((array, i, node) => {
|
||||
var object = node.get_object ();
|
||||
if (object != null)
|
||||
_attachments += Attachment.parse (object);
|
||||
});
|
||||
if (_attachments.length > 0)
|
||||
status.attachments = _attachments;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
@ -9,22 +9,30 @@ public class Tootle.CacheManager : GLib.Object{
|
||||
path_images = GLib.Environment.get_user_special_dir (UserDirectory.DOWNLOAD);
|
||||
}
|
||||
|
||||
//TODO: actually cache images
|
||||
public CacheManager(){
|
||||
Object ();
|
||||
}
|
||||
|
||||
//TODO: actually cache images
|
||||
public void load_avatar (string url, Granite.Widgets.Avatar avatar, int size){
|
||||
var msg = new Soup.Message("GET", url);
|
||||
msg.finished.connect(()=>{
|
||||
uint8[] buf = msg.response_body.data;
|
||||
msg.finished.connect(() => {
|
||||
var loader = new PixbufLoader();
|
||||
loader.set_size (size, size);
|
||||
loader.write(buf);
|
||||
loader.write(msg.response_body.data);
|
||||
loader.close();
|
||||
var pixbuf = loader.get_pixbuf ();
|
||||
|
||||
avatar.pixbuf = pixbuf;
|
||||
avatar.pixbuf = loader.get_pixbuf ();
|
||||
});
|
||||
Tootle.network.queue(msg, (sess, mess) => {});
|
||||
}
|
||||
|
||||
public void load_image (string url, Gtk.Image image){
|
||||
var msg = new Soup.Message("GET", url);
|
||||
msg.finished.connect(() => {
|
||||
var loader = new PixbufLoader();
|
||||
loader.write(msg.response_body.data);
|
||||
loader.close();
|
||||
image.set_from_pixbuf (loader.get_pixbuf ());
|
||||
});
|
||||
Tootle.network.queue(msg, (sess, mess) => {});
|
||||
}
|
||||
|
@ -1,6 +1,10 @@
|
||||
public class Tootle.Utils{
|
||||
|
||||
public static string escape_html(string content){
|
||||
public static void open_url (string url) {
|
||||
Gtk.show_uri (null, url, Gdk.CURRENT_TIME);
|
||||
}
|
||||
|
||||
public static string escape_html (string content) {
|
||||
var str = content
|
||||
.replace("<br>", "\n")
|
||||
.replace("</br>", "")
|
||||
|
@ -9,6 +9,7 @@ public abstract class Tootle.AbstractView : Gtk.ScrolledWindow {
|
||||
|
||||
construct {
|
||||
view = new Gtk.Box (Gtk.Orientation.VERTICAL, 0);
|
||||
view.valign = Gtk.Align.START;
|
||||
hscrollbar_policy = Gtk.PolicyType.NEVER;
|
||||
add (view);
|
||||
|
||||
|
@ -15,9 +15,9 @@ public class Tootle.StatusView : Tootle.AbstractView {
|
||||
if (is_root)
|
||||
widget.highlight ();
|
||||
|
||||
widget.content.selectable = true;
|
||||
if (widget.spoiler_content != null)
|
||||
widget.spoiler_content.selectable = true;
|
||||
widget.content_label.selectable = true;
|
||||
if (widget.content_spoiler != null)
|
||||
widget.content_spoiler.selectable = true;
|
||||
widget.avatar.button_press_event.connect(widget.on_avatar_clicked);
|
||||
view.pack_start (widget, false, false, 0);
|
||||
}
|
||||
|
44
src/Widgets/AttachmentWidget.vala
Normal file
44
src/Widgets/AttachmentWidget.vala
Normal file
@ -0,0 +1,44 @@
|
||||
using Gtk;
|
||||
|
||||
public class Tootle.AttachmentWidget : Gtk.EventBox {
|
||||
|
||||
Attachment attachment;
|
||||
Gtk.Grid grid;
|
||||
Gtk.Label? label;
|
||||
Gtk.Image? image;
|
||||
|
||||
construct {
|
||||
margin_top = 6;
|
||||
grid = new Gtk.Grid ();
|
||||
get_style_context ().add_class ("attachment");
|
||||
add (grid);
|
||||
}
|
||||
|
||||
public AttachmentWidget (Attachment att) {
|
||||
attachment = att;
|
||||
var type = attachment.type;
|
||||
|
||||
switch (type){
|
||||
case "image":
|
||||
image = new Gtk.Image ();
|
||||
image.vexpand = true;
|
||||
image.margin = 3;
|
||||
image.valign = Gtk.Align.CENTER;
|
||||
Tootle.cache.load_image (attachment.preview_url, image);
|
||||
grid.attach (image, 0, 0);
|
||||
break;
|
||||
default:
|
||||
label = new Gtk.Label (_("Click to open %s media").printf (type));
|
||||
label.margin = 16;
|
||||
grid.attach (label, 0, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
button_press_event.connect(() => {
|
||||
Tootle.Utils.open_url (attachment.url);
|
||||
return true;
|
||||
});
|
||||
show_all ();
|
||||
}
|
||||
|
||||
}
|
@ -12,9 +12,18 @@ public class Tootle.RichLabel : Gtk.Label {
|
||||
activate_link.connect (open_link);
|
||||
}
|
||||
|
||||
public void wrap_words () {
|
||||
halign = Gtk.Align.START;
|
||||
single_line_mode = false;
|
||||
set_line_wrap (true);
|
||||
wrap_mode = Pango.WrapMode.WORD_CHAR;
|
||||
justify = Gtk.Justification.LEFT;
|
||||
xalign = 0;
|
||||
}
|
||||
|
||||
public bool open_link (string url){
|
||||
if (mentions != null){
|
||||
foreach (Mention mention in mentions){
|
||||
foreach (Mention mention in mentions) {
|
||||
if (url == mention.url){
|
||||
AccountView.open_from_id (mention.id);
|
||||
return true;
|
||||
@ -34,7 +43,7 @@ public class Tootle.RichLabel : Gtk.Label {
|
||||
return true;
|
||||
}
|
||||
|
||||
Gtk.show_uri (null, url, Gdk.CURRENT_TIME);
|
||||
Tootle.Utils.open_url (url);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -13,9 +13,11 @@ public class Tootle.StatusWidget : Gtk.EventBox {
|
||||
public Gtk.Label title_date;
|
||||
public Gtk.Label title_acct;
|
||||
public Gtk.Revealer revealer;
|
||||
public Tootle.RichLabel content;
|
||||
public Gtk.Label? spoiler_content;
|
||||
public Tootle.RichLabel content_label;
|
||||
public Tootle.RichLabel content_spoiler;
|
||||
Gtk.Box title_box;
|
||||
Gtk.Box attachments;
|
||||
Gtk.ScrolledWindow attachments_scroll;
|
||||
Gtk.Grid grid;
|
||||
Gtk.Box counters;
|
||||
Gtk.Label reblogs;
|
||||
@ -51,20 +53,10 @@ public class Tootle.StatusWidget : Gtk.EventBox {
|
||||
title_date = new Gtk.Label ("");
|
||||
title_date.ellipsize = Pango.EllipsizeMode.END;
|
||||
title_box.pack_end (title_date, false, false, 0);
|
||||
|
||||
title_box.show_all ();
|
||||
|
||||
content = new RichLabel ("");
|
||||
content.halign = Gtk.Align.START;
|
||||
content.single_line_mode = false;
|
||||
content.set_line_wrap (true);
|
||||
content.wrap_mode = Pango.WrapMode.WORD_CHAR;
|
||||
content.justify = Gtk.Justification.LEFT;
|
||||
content.margin_end = 12;
|
||||
content.xalign = 0;
|
||||
revealer = new Revealer ();
|
||||
revealer.reveal_child = true;
|
||||
revealer.add (content);
|
||||
content_label = new RichLabel ("");
|
||||
content_label.wrap_words ();
|
||||
|
||||
reblogs = new Gtk.Label ("0");
|
||||
favorites = new Gtk.Label ("0");
|
||||
@ -88,6 +80,20 @@ public class Tootle.StatusWidget : Gtk.EventBox {
|
||||
PostDialog.open_reply (Tootle.window, this.status);
|
||||
});
|
||||
|
||||
attachments = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 6);
|
||||
attachments.hexpand = true;
|
||||
attachments_scroll = new ScrolledWindow (null, null);
|
||||
attachments_scroll.vscrollbar_policy = Gtk.PolicyType.NEVER;
|
||||
attachments_scroll.add (attachments);
|
||||
|
||||
var revealer_box = new Gtk.Box (Gtk.Orientation.VERTICAL, 6);
|
||||
revealer_box.margin_end = 12;
|
||||
revealer_box.add (content_label);
|
||||
revealer_box.add (attachments_scroll);
|
||||
revealer = new Revealer ();
|
||||
revealer.reveal_child = true;
|
||||
revealer.add (revealer_box);
|
||||
|
||||
counters = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 6); //TODO: currently useless
|
||||
counters.margin_top = 6;
|
||||
counters.margin_bottom = 6;
|
||||
@ -103,7 +109,7 @@ public class Tootle.StatusWidget : Gtk.EventBox {
|
||||
grid.attach (revealer, 2, 4, 1, 1);
|
||||
grid.attach (counters, 2, 5, 1, 1);
|
||||
add (grid);
|
||||
show_all (); //TODO: display conversations
|
||||
show_all ();
|
||||
}
|
||||
|
||||
public StatusWidget (Status status) {
|
||||
@ -128,24 +134,49 @@ public class Tootle.StatusWidget : Gtk.EventBox {
|
||||
grid.attach (label, 2, 0, 2, 1);
|
||||
}
|
||||
|
||||
if (status.spoiler_text != null){
|
||||
if (is_spoiler ()){
|
||||
revealer.reveal_child = false;
|
||||
spoiler_button = new Button.with_label (_("Toggle content"));
|
||||
spoiler_content = new RichLabel (status.spoiler_text);
|
||||
|
||||
var spoiler_box = new Box (Gtk.Orientation.HORIZONTAL, 6);
|
||||
spoiler_box.add (spoiler_content);
|
||||
spoiler_box.margin_end = 12;
|
||||
|
||||
var spoiler_button_text = _("Toggle content");
|
||||
if (status.sensitive && status.attachments != null) {
|
||||
spoiler_button = new Button.from_icon_name ("mail-attachment-symbolic", Gtk.IconSize.BUTTON);
|
||||
spoiler_button.label = spoiler_button_text;
|
||||
spoiler_button.always_show_image = true;
|
||||
spoiler_button.hexpand = true;
|
||||
spoiler_button.halign = Gtk.Align.END;
|
||||
content_label.margin_top = 6;
|
||||
}
|
||||
else {
|
||||
spoiler_button = new Button.with_label (spoiler_button_text);
|
||||
spoiler_button.hexpand = true;
|
||||
spoiler_button.halign = Gtk.Align.END;
|
||||
}
|
||||
spoiler_button.clicked.connect (() => revealer.set_reveal_child (!revealer.child_revealed));
|
||||
|
||||
var spoiler_text = _("[ This post contains sensitive content ]");
|
||||
if (status.spoiler_text != null)
|
||||
spoiler_text = status.spoiler_text;
|
||||
content_spoiler = new RichLabel (spoiler_text);
|
||||
content_spoiler.wrap_words ();
|
||||
|
||||
spoiler_box.add (content_spoiler);
|
||||
spoiler_box.add (spoiler_button);
|
||||
spoiler_box.show_all ();
|
||||
|
||||
spoiler_button.clicked.connect (() => revealer.set_reveal_child (!revealer.child_revealed));
|
||||
grid.attach (spoiler_box, 2, 3, 1, 1);
|
||||
}
|
||||
|
||||
if (status.attachments != null) {
|
||||
foreach (Attachment attachment in status.attachments)
|
||||
attachments.add (new AttachmentWidget (attachment));
|
||||
}
|
||||
|
||||
destroy.connect (() => {
|
||||
if(separator != null)
|
||||
separator.destroy ();
|
||||
});
|
||||
|
||||
rebind ();
|
||||
}
|
||||
|
||||
@ -157,8 +188,8 @@ public class Tootle.StatusWidget : Gtk.EventBox {
|
||||
public void rebind (){
|
||||
title_user.label = "<b>%s</b>".printf (status.get_formal ().account.display_name);
|
||||
title_acct.label = "@" + status.account.acct;
|
||||
content.label = status.content;
|
||||
content.mentions = status.mentions;
|
||||
content_label.label = status.content;
|
||||
content_label.mentions = status.mentions;
|
||||
parse_date_iso8601 (status.created_at);
|
||||
|
||||
reblogs.label = status.reblogs_count.to_string ();
|
||||
@ -173,6 +204,10 @@ public class Tootle.StatusWidget : Gtk.EventBox {
|
||||
|
||||
Tootle.cache.load_avatar (status.get_formal ().account.avatar, this.avatar, this.avatar_size);
|
||||
}
|
||||
|
||||
public bool is_spoiler (){
|
||||
return status.spoiler_text != null || status.sensitive;
|
||||
}
|
||||
|
||||
// elementary OS has old GLib, so I have to improvise
|
||||
// Version >=2.56 provides DateTime.from_iso8601
|
||||
|
Loading…
x
Reference in New Issue
Block a user