From 474f67b5a57f63d314d50126c7e7f2903ac5c7ff Mon Sep 17 00:00:00 2001 From: bleakgrey Date: Wed, 24 Oct 2018 15:01:32 +0300 Subject: [PATCH] Delete & Redraft (close #61) --- src/API/Status.vala | 18 +++--- src/Dialogs/PostDialog.vala | 104 ++++++++++++++++++++---------- src/Views/AbstractView.vala | 2 + src/Views/TimelineView.vala | 4 +- src/Widgets/AttachmentBox.vala | 3 +- src/Widgets/AttachmentWidget.vala | 5 +- src/Widgets/StatusWidget.vala | 18 ++++-- 7 files changed, 101 insertions(+), 53 deletions(-) diff --git a/src/API/Status.vala b/src/API/Status.vala index 48e7e98..2d32342 100644 --- a/src/API/Status.vala +++ b/src/API/Status.vala @@ -11,7 +11,7 @@ public class Tootle.Status { public int64 replies_count; public int64 reblogs_count; public int64 favourites_count; - public string created_at; + public string created_at; public bool reblogged = false; public bool favorited = false; public bool sensitive = false; @@ -30,7 +30,7 @@ public class Tootle.Status { return reblog != null ? reblog : this; } - public static Status parse(Json.Object obj) { + public static Status parse (Json.Object obj) { var id = int64.parse (obj.get_string_member ("id")); var status = new Status (id); @@ -108,7 +108,7 @@ public class Tootle.Status { return result; } - public void set_reblogged (bool rebl = true){ + public void set_reblogged (bool rebl = true) { var action = rebl ? "reblog" : "unreblog"; var msg = new Soup.Message ("POST", "%s/api/v1/statuses/%lld/%s".printf (accounts.formal.instance, id, action)); msg.priority = Soup.MessagePriority.HIGH; @@ -123,7 +123,7 @@ public class Tootle.Status { network.queue (msg); } - public void set_favorited (bool fav = true){ + public void set_favorited (bool fav = true) { var action = fav ? "favourite" : "unfavourite"; var msg = new Soup.Message ("POST", "%s/api/v1/statuses/%lld/%s".printf (accounts.formal.instance, id, action)); msg.priority = Soup.MessagePriority.HIGH; @@ -138,7 +138,7 @@ public class Tootle.Status { network.queue (msg); } - public void set_muted (bool mute = true){ + public void set_muted (bool mute = true) { var action = mute ? "mute" : "unmute"; var msg = new Soup.Message ("POST", "%s/api/v1/statuses/%lld/%s".printf (accounts.formal.instance, id, action)); msg.priority = Soup.MessagePriority.HIGH; @@ -153,7 +153,7 @@ public class Tootle.Status { network.queue (msg); } - public void set_pinned (bool pin = true){ + public void set_pinned (bool pin = true) { var action = pin ? "pin" : "unpin"; var msg = new Soup.Message ("POST", "%s/api/v1/statuses/%lld/%s".printf (accounts.formal.instance, id, action)); msg.priority = Soup.MessagePriority.HIGH; @@ -168,14 +168,16 @@ public class Tootle.Status { network.queue (msg); } - public void poof (){ + public Soup.Message poof (bool show_toast = true) { var msg = new Soup.Message ("DELETE", "%s/api/v1/statuses/%lld".printf (accounts.formal.instance, id)); msg.priority = Soup.MessagePriority.HIGH; msg.finished.connect (() => { - app.toast (_("Poof!")); + if (show_toast) + app.toast (_("Poof!")); network.status_removed (id); }); network.queue (msg); + return msg; } } diff --git a/src/Dialogs/PostDialog.vala b/src/Dialogs/PostDialog.vala index 963bb1d..1c4d24b 100644 --- a/src/Dialogs/PostDialog.vala +++ b/src/Dialogs/PostDialog.vala @@ -2,7 +2,7 @@ using Gtk; using Tootle; public class Tootle.PostDialog : Gtk.Dialog { - + private static PostDialog dialog; protected TextView text; @@ -13,25 +13,29 @@ public class Tootle.PostDialog : Gtk.Dialog { private Button attach; private Button cancel; private Button publish; - private AttachmentBox attachments; + protected AttachmentBox attachments; private Revealer spoiler_revealer; private Entry spoiler_text; - protected Status? in_reply_to; + protected Status? replying_to; + protected Status? redrafting; protected StatusVisibility visibility_opt = StatusVisibility.PUBLIC; protected int char_limit; - public PostDialog (Status? status = null) { + public PostDialog (Status? _replying_to = null, Status? _redrafting = null) { border_width = 6; deletable = false; resizable = true; title = _("Toot"); transient_for = window; - char_limit = settings.char_limit; - in_reply_to = status; - if (in_reply_to != null) - visibility_opt = in_reply_to.visibility; + replying_to = _replying_to; + redrafting = _redrafting; + + if (replying_to != null) + visibility_opt = replying_to.visibility; + if (redrafting != null) + visibility_opt = redrafting.visibility; var actions = get_action_area ().get_parent () as Gtk.Box; var content = get_content_area (); @@ -42,15 +46,15 @@ public class Tootle.PostDialog : Gtk.Dialog { visibility.get_style_context ().add_class (Gtk.STYLE_CLASS_FLAT); visibility.get_style_context ().remove_class ("image-button"); visibility.can_default = false; - (visibility as Gtk.Widget).set_focus_on_click (false); + (visibility as Widget).set_focus_on_click (false); - attach = new Gtk.Button.from_icon_name ("mail-attachment-symbolic"); + attach = new Button.from_icon_name ("mail-attachment-symbolic"); attach.tooltip_text = _("Add Media"); attach.valign = Gtk.Align.CENTER; attach.get_style_context ().add_class (Gtk.STYLE_CLASS_FLAT); attach.get_style_context ().remove_class ("image-button"); attach.can_default = false; - (attach as Gtk.Widget).set_focus_on_click (false); + (attach as Widget).set_focus_on_click (false); attach.clicked.connect (() => attachments.select ()); spoiler = new ImageToggleButton ("image-red-eye-symbolic"); @@ -61,13 +65,19 @@ public class Tootle.PostDialog : Gtk.Dialog { validate (); }); - cancel = add_button(_("Cancel"), 5) as Gtk.Button; - cancel.clicked.connect(() => this.destroy ()); - publish = add_button(_("Toot!"), 5) as Gtk.Button; - publish.get_style_context ().add_class (Gtk.STYLE_CLASS_SUGGESTED_ACTION); - publish.clicked.connect (() => { - publish_post (); - }); + cancel = add_button (_("Cancel"), 5) as Button; + cancel.clicked.connect(() => destroy ()); + + if (redrafting != null) { + publish = add_button (_("Redraft"), 5) as Button; + publish.get_style_context ().add_class (Gtk.STYLE_CLASS_DESTRUCTIVE_ACTION); + publish.clicked.connect (redraft_post); + } + else { + publish = add_button (_("Toot!"), 5) as Button; + publish.get_style_context ().add_class (Gtk.STYLE_CLASS_SUGGESTED_ACTION); + publish.clicked.connect (publish_post); + } spoiler_text = new Gtk.Entry (); spoiler_text.margin_start = 6; @@ -78,14 +88,14 @@ public class Tootle.PostDialog : Gtk.Dialog { spoiler_revealer = new Gtk.Revealer (); spoiler_revealer.add (spoiler_text); - text = new Gtk.TextView (); + text = new TextView (); text.get_style_context ().add_class ("toot-text"); text.wrap_mode = Gtk.WrapMode.WORD; text.accepts_tab = false; text.vexpand = true; text.buffer.changed.connect (validate); - scroll = new Gtk.ScrolledWindow (null, null); + scroll = new ScrolledWindow (null, null); scroll.hscrollbar_policy = Gtk.PolicyType.NEVER; scroll.min_content_height = 120; scroll.vexpand = true; @@ -96,7 +106,7 @@ public class Tootle.PostDialog : Gtk.Dialog { scroll.show_all (); attachments = new AttachmentBox (true); - counter = new Gtk.Label (""); + counter = new Label (""); actions.pack_start (counter, false, false, 6); actions.pack_end (spoiler, false, false, 6); @@ -107,11 +117,18 @@ public class Tootle.PostDialog : Gtk.Dialog { content.pack_start (attachments, false, false, 6); content.set_size_request (350, 120); - if (in_reply_to != null) { - spoiler.active = in_reply_to.sensitive; - var status_spoiler_text = in_reply_to.spoiler_text != null ? in_reply_to.spoiler_text : ""; + if (replying_to != null) { + spoiler.active = replying_to.sensitive; + var status_spoiler_text = replying_to.spoiler_text != null ? replying_to.spoiler_text : ""; spoiler_text.set_text (status_spoiler_text); } + if (redrafting != null) { + spoiler.active = redrafting.sensitive; + var status_spoiler_text = redrafting.spoiler_text != null ? redrafting.spoiler_text : ""; + spoiler_text.set_text (status_spoiler_text); + } + + destroy.connect (() => dialog = null); show_all (); attachments.hide (); @@ -162,7 +179,7 @@ public class Tootle.PostDialog : Gtk.Dialog { public static void open (string? text = null, Status? reply_to = null) { if (dialog == null){ dialog = new PostDialog (reply_to); - dialog.destroy.connect (() => dialog = null); + if (text != null) dialog.text.buffer.text = text; } @@ -170,19 +187,34 @@ public class Tootle.PostDialog : Gtk.Dialog { dialog.text.buffer.text += text; } - public static void open_reply (Status reply_to) { - if(dialog != null) + public static void reply (Status status) { + if (dialog != null) return; - open (null, reply_to); - dialog.text.buffer.text = reply_to.get_reply_mentions (); + open (null, status); + dialog.text.buffer.text = status.get_reply_mentions (); } - public void publish_post () { + public static void redraft (Status status) { + if (dialog != null) + return; + dialog = new PostDialog (null, status); + + if (status.attachments != null) { + foreach (Attachment attachment in status.attachments) + dialog.attachments.append (attachment); + } + + var content = Html.simplify (status.content); + content = Html.remove_tags (content); + dialog.text.buffer.text = content; + } + + private void publish_post () { var pars = "?status=%s&visibility=%s".printf (Html.uri_encode (text.buffer.text), visibility_opt.to_string ()); pars += attachments.get_uri_array (); - if (in_reply_to != null) - pars += "&in_reply_to_id=%s".printf (in_reply_to.id.to_string ()); + if (replying_to != null) + pars += "&in_reply_to_id=%s".printf (replying_to.id.to_string ()); if (spoiler.active) { pars += "&sensitive=true"; @@ -191,12 +223,12 @@ public class Tootle.PostDialog : Gtk.Dialog { var url = "%s/api/v1/statuses%s".printf (accounts.formal.instance, pars); var msg = new Soup.Message ("POST", url); - network.queue(msg, (sess, mess) => { + network.queue (msg, (sess, mess) => { try { var root = network.parse (mess); var status = Status.parse (root); debug ("Posted: %s", status.id.to_string ()); //TODO: Live updates - this.destroy (); + destroy (); } catch (GLib.Error e) { warning ("Can't publish post."); @@ -204,5 +236,9 @@ public class Tootle.PostDialog : Gtk.Dialog { } }); } + + private void redraft_post () { + redrafting.poof ().finished.connect (publish_post); + } } diff --git a/src/Views/AbstractView.vala b/src/Views/AbstractView.vala index 6d64962..685cddc 100644 --- a/src/Views/AbstractView.vala +++ b/src/Views/AbstractView.vala @@ -10,7 +10,9 @@ public abstract class Tootle.AbstractView : ScrolledWindow { construct { view = new Box (Orientation.VERTICAL, 0); + view.valign = Align.START; add (view); + hscrollbar_policy = PolicyType.NEVER; edge_reached.connect(pos => { if (pos == PositionType.BOTTOM) diff --git a/src/Views/TimelineView.vala b/src/Views/TimelineView.vala index a18b5be..03425fe 100644 --- a/src/Views/TimelineView.vala +++ b/src/Views/TimelineView.vala @@ -52,7 +52,7 @@ public class Tootle.TimelineView : AbstractView { if (empty != null) empty.destroy (); - var separator = new Gtk.Separator (Gtk.Orientation.HORIZONTAL); + var separator = new Separator (Orientation.HORIZONTAL); separator.show (); var widget = new StatusWidget (status); @@ -65,8 +65,8 @@ public class Tootle.TimelineView : AbstractView { if (first || status.pinned) { var new_index = header == null ? 1 : 0; - view.reorder_child (widget, new_index); view.reorder_child (separator, new_index); + view.reorder_child (widget, new_index); } } diff --git a/src/Widgets/AttachmentBox.vala b/src/Widgets/AttachmentBox.vala index 11d744f..f996a83 100644 --- a/src/Widgets/AttachmentBox.vala +++ b/src/Widgets/AttachmentBox.vala @@ -24,7 +24,8 @@ public class Tootle.AttachmentBox : Gtk.ScrolledWindow { } public void append (Attachment attachment) { - var widget = new AttachmentWidget (attachment); + show (); + var widget = new AttachmentWidget (attachment, edit_mode); box.add (widget); } diff --git a/src/Widgets/AttachmentWidget.vala b/src/Widgets/AttachmentWidget.vala index 6baf72b..cb85dd1 100644 --- a/src/Widgets/AttachmentWidget.vala +++ b/src/Widgets/AttachmentWidget.vala @@ -4,7 +4,7 @@ using Gdk; public class Tootle.AttachmentWidget : Gtk.EventBox { public Attachment? attachment; - private bool editable = false; + private bool editable; private const int PREVIEW_SIZE = 350; private const int SMALL_SIZE = 64; @@ -34,8 +34,9 @@ public class Tootle.AttachmentWidget : Gtk.EventBox { }); } - public AttachmentWidget (Attachment att) { + public AttachmentWidget (Attachment att, bool _editable = false) { attachment = att; + editable = _editable; rebind (); } diff --git a/src/Widgets/StatusWidget.vala b/src/Widgets/StatusWidget.vala index 4dbd328..092072d 100644 --- a/src/Widgets/StatusWidget.vala +++ b/src/Widgets/StatusWidget.vala @@ -98,7 +98,7 @@ public class Tootle.StatusWidget : Gtk.EventBox { reply.tooltip_text = _("Reply"); reply.toggled.connect (() => { reply.set_active (false); - PostDialog.open_reply (status.get_formal ()); + PostDialog.reply (status.get_formal ()); }); counters = new Box (Orientation.HORIZONTAL, 6); @@ -189,7 +189,7 @@ public class Tootle.StatusWidget : Gtk.EventBox { }); network.status_removed.connect (id => { - if (id == this.status.id) + if (id == status.id) destroy (); }); @@ -286,10 +286,6 @@ public class Tootle.StatusWidget : Gtk.EventBox { var item_muting = new Gtk.MenuItem.with_label (is_muted ? _("Unmute Conversation") : _("Mute Conversation")); item_muting.activate.connect (() => status.set_muted (!is_muted)); - var item_pin = new Gtk.MenuItem.with_label (is_pinned ? _("Unpin from Profile") : _("Pin on Profile")); - item_pin.activate.connect (() => status.set_pinned (!is_pinned)); - var item_delete = new Gtk.MenuItem.with_label (_("Delete")); - item_delete.activate.connect (() => status.poof ()); var item_open_link = new Gtk.MenuItem.with_label (_("Open in Browser")); item_open_link.activate.connect (() => Desktop.open_uri (status.get_formal ().url)); var item_copy_link = new Gtk.MenuItem.with_label (_("Copy Link")); @@ -301,8 +297,18 @@ public class Tootle.StatusWidget : Gtk.EventBox { }); if (this.status.is_owned ()) { + var item_pin = new Gtk.MenuItem.with_label (is_pinned ? _("Unpin from Profile") : _("Pin on Profile")); + item_pin.activate.connect (() => status.set_pinned (!is_pinned)); menu.add (item_pin); + + var item_delete = new Gtk.MenuItem.with_label (_("Delete")); + item_delete.activate.connect (() => status.poof ()); menu.add (item_delete); + + var item_redraft = new Gtk.MenuItem.with_label (_("Redraft")); + item_redraft.activate.connect (() => PostDialog.redraft (status.get_formal ())); + menu.add (item_redraft); + menu.add (new Gtk.SeparatorMenuItem ()); }