Delete & Redraft (close #61)

This commit is contained in:
bleakgrey 2018-10-24 15:01:32 +03:00
parent adb44697bf
commit 474f67b5a5
7 changed files with 101 additions and 53 deletions

View File

@ -11,7 +11,7 @@ public class Tootle.Status {
public int64 replies_count; public int64 replies_count;
public int64 reblogs_count; public int64 reblogs_count;
public int64 favourites_count; public int64 favourites_count;
public string created_at; public string created_at;
public bool reblogged = false; public bool reblogged = false;
public bool favorited = false; public bool favorited = false;
public bool sensitive = false; public bool sensitive = false;
@ -30,7 +30,7 @@ public class Tootle.Status {
return reblog != null ? reblog : this; 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 id = int64.parse (obj.get_string_member ("id"));
var status = new Status (id); var status = new Status (id);
@ -108,7 +108,7 @@ public class Tootle.Status {
return result; return result;
} }
public void set_reblogged (bool rebl = true){ public void set_reblogged (bool rebl = true) {
var action = rebl ? "reblog" : "unreblog"; var action = rebl ? "reblog" : "unreblog";
var msg = new Soup.Message ("POST", "%s/api/v1/statuses/%lld/%s".printf (accounts.formal.instance, id, action)); var msg = new Soup.Message ("POST", "%s/api/v1/statuses/%lld/%s".printf (accounts.formal.instance, id, action));
msg.priority = Soup.MessagePriority.HIGH; msg.priority = Soup.MessagePriority.HIGH;
@ -123,7 +123,7 @@ public class Tootle.Status {
network.queue (msg); network.queue (msg);
} }
public void set_favorited (bool fav = true){ public void set_favorited (bool fav = true) {
var action = fav ? "favourite" : "unfavourite"; var action = fav ? "favourite" : "unfavourite";
var msg = new Soup.Message ("POST", "%s/api/v1/statuses/%lld/%s".printf (accounts.formal.instance, id, action)); var msg = new Soup.Message ("POST", "%s/api/v1/statuses/%lld/%s".printf (accounts.formal.instance, id, action));
msg.priority = Soup.MessagePriority.HIGH; msg.priority = Soup.MessagePriority.HIGH;
@ -138,7 +138,7 @@ public class Tootle.Status {
network.queue (msg); network.queue (msg);
} }
public void set_muted (bool mute = true){ public void set_muted (bool mute = true) {
var action = mute ? "mute" : "unmute"; var action = mute ? "mute" : "unmute";
var msg = new Soup.Message ("POST", "%s/api/v1/statuses/%lld/%s".printf (accounts.formal.instance, id, action)); var msg = new Soup.Message ("POST", "%s/api/v1/statuses/%lld/%s".printf (accounts.formal.instance, id, action));
msg.priority = Soup.MessagePriority.HIGH; msg.priority = Soup.MessagePriority.HIGH;
@ -153,7 +153,7 @@ public class Tootle.Status {
network.queue (msg); network.queue (msg);
} }
public void set_pinned (bool pin = true){ public void set_pinned (bool pin = true) {
var action = pin ? "pin" : "unpin"; var action = pin ? "pin" : "unpin";
var msg = new Soup.Message ("POST", "%s/api/v1/statuses/%lld/%s".printf (accounts.formal.instance, id, action)); var msg = new Soup.Message ("POST", "%s/api/v1/statuses/%lld/%s".printf (accounts.formal.instance, id, action));
msg.priority = Soup.MessagePriority.HIGH; msg.priority = Soup.MessagePriority.HIGH;
@ -168,14 +168,16 @@ public class Tootle.Status {
network.queue (msg); 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)); var msg = new Soup.Message ("DELETE", "%s/api/v1/statuses/%lld".printf (accounts.formal.instance, id));
msg.priority = Soup.MessagePriority.HIGH; msg.priority = Soup.MessagePriority.HIGH;
msg.finished.connect (() => { msg.finished.connect (() => {
app.toast (_("Poof!")); if (show_toast)
app.toast (_("Poof!"));
network.status_removed (id); network.status_removed (id);
}); });
network.queue (msg); network.queue (msg);
return msg;
} }
} }

View File

@ -2,7 +2,7 @@ using Gtk;
using Tootle; using Tootle;
public class Tootle.PostDialog : Gtk.Dialog { public class Tootle.PostDialog : Gtk.Dialog {
private static PostDialog dialog; private static PostDialog dialog;
protected TextView text; protected TextView text;
@ -13,25 +13,29 @@ public class Tootle.PostDialog : Gtk.Dialog {
private Button attach; private Button attach;
private Button cancel; private Button cancel;
private Button publish; private Button publish;
private AttachmentBox attachments; protected AttachmentBox attachments;
private Revealer spoiler_revealer; private Revealer spoiler_revealer;
private Entry spoiler_text; private Entry spoiler_text;
protected Status? in_reply_to; protected Status? replying_to;
protected Status? redrafting;
protected StatusVisibility visibility_opt = StatusVisibility.PUBLIC; protected StatusVisibility visibility_opt = StatusVisibility.PUBLIC;
protected int char_limit; protected int char_limit;
public PostDialog (Status? status = null) { public PostDialog (Status? _replying_to = null, Status? _redrafting = null) {
border_width = 6; border_width = 6;
deletable = false; deletable = false;
resizable = true; resizable = true;
title = _("Toot"); title = _("Toot");
transient_for = window; transient_for = window;
char_limit = settings.char_limit; char_limit = settings.char_limit;
in_reply_to = status; replying_to = _replying_to;
if (in_reply_to != null) redrafting = _redrafting;
visibility_opt = in_reply_to.visibility;
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 actions = get_action_area ().get_parent () as Gtk.Box;
var content = get_content_area (); 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 ().add_class (Gtk.STYLE_CLASS_FLAT);
visibility.get_style_context ().remove_class ("image-button"); visibility.get_style_context ().remove_class ("image-button");
visibility.can_default = false; 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.tooltip_text = _("Add Media");
attach.valign = Gtk.Align.CENTER; attach.valign = Gtk.Align.CENTER;
attach.get_style_context ().add_class (Gtk.STYLE_CLASS_FLAT); attach.get_style_context ().add_class (Gtk.STYLE_CLASS_FLAT);
attach.get_style_context ().remove_class ("image-button"); attach.get_style_context ().remove_class ("image-button");
attach.can_default = false; 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 ()); attach.clicked.connect (() => attachments.select ());
spoiler = new ImageToggleButton ("image-red-eye-symbolic"); spoiler = new ImageToggleButton ("image-red-eye-symbolic");
@ -61,13 +65,19 @@ public class Tootle.PostDialog : Gtk.Dialog {
validate (); validate ();
}); });
cancel = add_button(_("Cancel"), 5) as Gtk.Button; cancel = add_button (_("Cancel"), 5) as Button;
cancel.clicked.connect(() => this.destroy ()); cancel.clicked.connect(() => destroy ());
publish = add_button(_("Toot!"), 5) as Gtk.Button;
publish.get_style_context ().add_class (Gtk.STYLE_CLASS_SUGGESTED_ACTION); if (redrafting != null) {
publish.clicked.connect (() => { publish = add_button (_("Redraft"), 5) as Button;
publish_post (); 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 = new Gtk.Entry ();
spoiler_text.margin_start = 6; spoiler_text.margin_start = 6;
@ -78,14 +88,14 @@ public class Tootle.PostDialog : Gtk.Dialog {
spoiler_revealer = new Gtk.Revealer (); spoiler_revealer = new Gtk.Revealer ();
spoiler_revealer.add (spoiler_text); spoiler_revealer.add (spoiler_text);
text = new Gtk.TextView (); text = new TextView ();
text.get_style_context ().add_class ("toot-text"); text.get_style_context ().add_class ("toot-text");
text.wrap_mode = Gtk.WrapMode.WORD; text.wrap_mode = Gtk.WrapMode.WORD;
text.accepts_tab = false; text.accepts_tab = false;
text.vexpand = true; text.vexpand = true;
text.buffer.changed.connect (validate); text.buffer.changed.connect (validate);
scroll = new Gtk.ScrolledWindow (null, null); scroll = new ScrolledWindow (null, null);
scroll.hscrollbar_policy = Gtk.PolicyType.NEVER; scroll.hscrollbar_policy = Gtk.PolicyType.NEVER;
scroll.min_content_height = 120; scroll.min_content_height = 120;
scroll.vexpand = true; scroll.vexpand = true;
@ -96,7 +106,7 @@ public class Tootle.PostDialog : Gtk.Dialog {
scroll.show_all (); scroll.show_all ();
attachments = new AttachmentBox (true); attachments = new AttachmentBox (true);
counter = new Gtk.Label (""); counter = new Label ("");
actions.pack_start (counter, false, false, 6); actions.pack_start (counter, false, false, 6);
actions.pack_end (spoiler, 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.pack_start (attachments, false, false, 6);
content.set_size_request (350, 120); content.set_size_request (350, 120);
if (in_reply_to != null) { if (replying_to != null) {
spoiler.active = in_reply_to.sensitive; spoiler.active = replying_to.sensitive;
var status_spoiler_text = in_reply_to.spoiler_text != null ? in_reply_to.spoiler_text : ""; var status_spoiler_text = replying_to.spoiler_text != null ? replying_to.spoiler_text : "";
spoiler_text.set_text (status_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 (); show_all ();
attachments.hide (); attachments.hide ();
@ -162,7 +179,7 @@ public class Tootle.PostDialog : Gtk.Dialog {
public static void open (string? text = null, Status? reply_to = null) { public static void open (string? text = null, Status? reply_to = null) {
if (dialog == null){ if (dialog == null){
dialog = new PostDialog (reply_to); dialog = new PostDialog (reply_to);
dialog.destroy.connect (() => dialog = null);
if (text != null) if (text != null)
dialog.text.buffer.text = text; dialog.text.buffer.text = text;
} }
@ -170,19 +187,34 @@ public class Tootle.PostDialog : Gtk.Dialog {
dialog.text.buffer.text += text; dialog.text.buffer.text += text;
} }
public static void open_reply (Status reply_to) { public static void reply (Status status) {
if(dialog != null) if (dialog != null)
return; return;
open (null, reply_to); open (null, status);
dialog.text.buffer.text = reply_to.get_reply_mentions (); 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 ()); var pars = "?status=%s&visibility=%s".printf (Html.uri_encode (text.buffer.text), visibility_opt.to_string ());
pars += attachments.get_uri_array (); pars += attachments.get_uri_array ();
if (in_reply_to != null) if (replying_to != null)
pars += "&in_reply_to_id=%s".printf (in_reply_to.id.to_string ()); pars += "&in_reply_to_id=%s".printf (replying_to.id.to_string ());
if (spoiler.active) { if (spoiler.active) {
pars += "&sensitive=true"; 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 url = "%s/api/v1/statuses%s".printf (accounts.formal.instance, pars);
var msg = new Soup.Message ("POST", url); var msg = new Soup.Message ("POST", url);
network.queue(msg, (sess, mess) => { network.queue (msg, (sess, mess) => {
try { try {
var root = network.parse (mess); var root = network.parse (mess);
var status = Status.parse (root); var status = Status.parse (root);
debug ("Posted: %s", status.id.to_string ()); //TODO: Live updates debug ("Posted: %s", status.id.to_string ()); //TODO: Live updates
this.destroy (); destroy ();
} }
catch (GLib.Error e) { catch (GLib.Error e) {
warning ("Can't publish post."); 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);
}
} }

View File

@ -10,7 +10,9 @@ public abstract class Tootle.AbstractView : ScrolledWindow {
construct { construct {
view = new Box (Orientation.VERTICAL, 0); view = new Box (Orientation.VERTICAL, 0);
view.valign = Align.START;
add (view); add (view);
hscrollbar_policy = PolicyType.NEVER; hscrollbar_policy = PolicyType.NEVER;
edge_reached.connect(pos => { edge_reached.connect(pos => {
if (pos == PositionType.BOTTOM) if (pos == PositionType.BOTTOM)

View File

@ -52,7 +52,7 @@ public class Tootle.TimelineView : AbstractView {
if (empty != null) if (empty != null)
empty.destroy (); empty.destroy ();
var separator = new Gtk.Separator (Gtk.Orientation.HORIZONTAL); var separator = new Separator (Orientation.HORIZONTAL);
separator.show (); separator.show ();
var widget = new StatusWidget (status); var widget = new StatusWidget (status);
@ -65,8 +65,8 @@ public class Tootle.TimelineView : AbstractView {
if (first || status.pinned) { if (first || status.pinned) {
var new_index = header == null ? 1 : 0; var new_index = header == null ? 1 : 0;
view.reorder_child (widget, new_index);
view.reorder_child (separator, new_index); view.reorder_child (separator, new_index);
view.reorder_child (widget, new_index);
} }
} }

View File

@ -24,7 +24,8 @@ public class Tootle.AttachmentBox : Gtk.ScrolledWindow {
} }
public void append (Attachment attachment) { public void append (Attachment attachment) {
var widget = new AttachmentWidget (attachment); show ();
var widget = new AttachmentWidget (attachment, edit_mode);
box.add (widget); box.add (widget);
} }

View File

@ -4,7 +4,7 @@ using Gdk;
public class Tootle.AttachmentWidget : Gtk.EventBox { public class Tootle.AttachmentWidget : Gtk.EventBox {
public Attachment? attachment; public Attachment? attachment;
private bool editable = false; private bool editable;
private const int PREVIEW_SIZE = 350; private const int PREVIEW_SIZE = 350;
private const int SMALL_SIZE = 64; 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; attachment = att;
editable = _editable;
rebind (); rebind ();
} }

View File

@ -98,7 +98,7 @@ public class Tootle.StatusWidget : Gtk.EventBox {
reply.tooltip_text = _("Reply"); reply.tooltip_text = _("Reply");
reply.toggled.connect (() => { reply.toggled.connect (() => {
reply.set_active (false); reply.set_active (false);
PostDialog.open_reply (status.get_formal ()); PostDialog.reply (status.get_formal ());
}); });
counters = new Box (Orientation.HORIZONTAL, 6); counters = new Box (Orientation.HORIZONTAL, 6);
@ -189,7 +189,7 @@ public class Tootle.StatusWidget : Gtk.EventBox {
}); });
network.status_removed.connect (id => { network.status_removed.connect (id => {
if (id == this.status.id) if (id == status.id)
destroy (); 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")); var item_muting = new Gtk.MenuItem.with_label (is_muted ? _("Unmute Conversation") : _("Mute Conversation"));
item_muting.activate.connect (() => status.set_muted (!is_muted)); 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")); var item_open_link = new Gtk.MenuItem.with_label (_("Open in Browser"));
item_open_link.activate.connect (() => Desktop.open_uri (status.get_formal ().url)); item_open_link.activate.connect (() => Desktop.open_uri (status.get_formal ().url));
var item_copy_link = new Gtk.MenuItem.with_label (_("Copy Link")); 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 ()) { 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); menu.add (item_pin);
var item_delete = new Gtk.MenuItem.with_label (_("Delete"));
item_delete.activate.connect (() => status.poof ());
menu.add (item_delete); 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 ()); menu.add (new Gtk.SeparatorMenuItem ());
} }