mirror of
https://github.com/ihabunek/toot.git
synced 2024-12-04 14:46:33 -05:00
TUI: Implement deleting own status messages
This commit is contained in:
parent
7309e5bb53
commit
1d3ff87ffa
@ -1,4 +1,9 @@
|
||||
|
||||
0.24.0:
|
||||
date: TBA
|
||||
changes:
|
||||
- "TUI: Implement deleting own status messages"
|
||||
|
||||
0.23.1:
|
||||
date: 2019-09-04
|
||||
changes:
|
||||
|
@ -9,6 +9,7 @@ from .compose import StatusComposer
|
||||
from .constants import PALETTE
|
||||
from .entities import Status
|
||||
from .overlays import ExceptionStackTrace, GotoMenu, Help, StatusSource
|
||||
from .overlays import StatusDeleteConfirmation
|
||||
from .timeline import Timeline
|
||||
from .utils import show_media
|
||||
|
||||
@ -165,8 +166,11 @@ class TUI(urwid.Frame):
|
||||
def _compose(*args):
|
||||
self.show_compose()
|
||||
|
||||
def _delete(timeline, status):
|
||||
if status.is_mine:
|
||||
self.show_delete_confirmation(status)
|
||||
|
||||
def _reply(timeline, status):
|
||||
logger.info("reply")
|
||||
self.show_compose(status)
|
||||
|
||||
def _source(timeline, status):
|
||||
@ -178,14 +182,15 @@ class TUI(urwid.Frame):
|
||||
def _menu(timeline, status):
|
||||
self.show_context_menu(status)
|
||||
|
||||
urwid.connect_signal(timeline, "focus", self.refresh_footer)
|
||||
urwid.connect_signal(timeline, "reblog", self.async_toggle_reblog)
|
||||
urwid.connect_signal(timeline, "favourite", self.async_toggle_favourite)
|
||||
urwid.connect_signal(timeline, "source", _source)
|
||||
urwid.connect_signal(timeline, "compose", _compose)
|
||||
urwid.connect_signal(timeline, "reply", _reply)
|
||||
urwid.connect_signal(timeline, "delete", _delete)
|
||||
urwid.connect_signal(timeline, "favourite", self.async_toggle_favourite)
|
||||
urwid.connect_signal(timeline, "focus", self.refresh_footer)
|
||||
urwid.connect_signal(timeline, "media", _media)
|
||||
urwid.connect_signal(timeline, "menu", _menu)
|
||||
urwid.connect_signal(timeline, "reblog", self.async_toggle_reblog)
|
||||
urwid.connect_signal(timeline, "reply", _reply)
|
||||
urwid.connect_signal(timeline, "source", _source)
|
||||
|
||||
def build_timeline(self, name, statuses):
|
||||
def _close(*args):
|
||||
@ -206,6 +211,10 @@ class TUI(urwid.Frame):
|
||||
|
||||
return timeline
|
||||
|
||||
def make_status(self, status_data):
|
||||
is_mine = self.user.username == status_data["account"]["acct"]
|
||||
return Status(status_data, is_mine, self.app.instance)
|
||||
|
||||
def show_thread(self, status):
|
||||
def _close(*args):
|
||||
"""When thread is closed, go back to the main timeline."""
|
||||
@ -216,8 +225,8 @@ class TUI(urwid.Frame):
|
||||
# This is pretty fast, so it's probably ok to block while context is
|
||||
# loaded, can be made async later if needed
|
||||
context = api.context(self.app, self.user, status.id)
|
||||
ancestors = [Status(s, self.app.instance) for s in context["ancestors"]]
|
||||
descendants = [Status(s, self.app.instance) for s in context["descendants"]]
|
||||
ancestors = [self.make_status(s) for s in context["ancestors"]]
|
||||
descendants = [self.make_status(s) for s in context["descendants"]]
|
||||
statuses = ancestors + [status] + descendants
|
||||
focus = len(ancestors)
|
||||
|
||||
@ -241,7 +250,7 @@ class TUI(urwid.Frame):
|
||||
finally:
|
||||
self.footer.clear_message()
|
||||
|
||||
return [Status(s, self.app.instance) for s in data]
|
||||
return [self.make_status(s) for s in data]
|
||||
|
||||
def _done_initial(statuses):
|
||||
"""Process initial batch of statuses, construct a Timeline."""
|
||||
@ -334,12 +343,28 @@ class TUI(urwid.Frame):
|
||||
# TODO: show context menu
|
||||
pass
|
||||
|
||||
def show_delete_confirmation(self, status):
|
||||
def _delete(widget):
|
||||
promise = self.async_delete_status(self.timeline, status)
|
||||
promise.add_done_callback(lambda *args: self.close_overlay())
|
||||
|
||||
def _close(widget):
|
||||
self.close_overlay()
|
||||
|
||||
widget = StatusDeleteConfirmation(status)
|
||||
urwid.connect_signal(widget, "close", _close)
|
||||
urwid.connect_signal(widget, "delete", _delete)
|
||||
self.open_overlay(widget, title="Delete status?", options=dict(
|
||||
align="center", width=("relative", 60),
|
||||
valign="middle", height=5,
|
||||
))
|
||||
|
||||
def post_status(self, content, warning, visibility, in_reply_to_id):
|
||||
data = api.post_status(self.app, self.user, content,
|
||||
spoiler_text=warning,
|
||||
visibility=visibility,
|
||||
in_reply_to_id=in_reply_to_id)
|
||||
status = Status(data, self.app.instance)
|
||||
status = self.make_status(data)
|
||||
|
||||
# TODO: instead of this, fetch new items from the timeline?
|
||||
self.timeline.prepend_status(status)
|
||||
@ -361,7 +386,8 @@ class TUI(urwid.Frame):
|
||||
# Create a new Status with flipped favourited flag
|
||||
new_data = status.data
|
||||
new_data["favourited"] = not status.favourited
|
||||
timeline.update_status(Status(new_data, status.instance))
|
||||
new_status = self.make_status(new_data)
|
||||
timeline.update_status(new_status)
|
||||
|
||||
self.run_in_thread(
|
||||
_unfavourite if status.favourited else _favourite,
|
||||
@ -381,13 +407,23 @@ class TUI(urwid.Frame):
|
||||
# Create a new Status with flipped reblogged flag
|
||||
new_data = status.data
|
||||
new_data["reblogged"] = not status.reblogged
|
||||
timeline.update_status(Status(new_data, status.instance))
|
||||
new_status = self.make_status(new_data)
|
||||
timeline.update_status(new_status)
|
||||
|
||||
self.run_in_thread(
|
||||
_unreblog if status.reblogged else _reblog,
|
||||
done_callback=_done
|
||||
)
|
||||
|
||||
def async_delete_status(self, timeline, status):
|
||||
def _delete():
|
||||
api.delete_status(self.app, self.user, status.id)
|
||||
|
||||
def _done(loop):
|
||||
timeline.remove_status(status)
|
||||
|
||||
return self.run_in_thread(_delete, done_callback=_done)
|
||||
|
||||
# --- Overlay handling -----------------------------------------------------
|
||||
|
||||
default_overlay_options = dict(
|
||||
|
@ -6,23 +6,16 @@ from .utils import parse_datetime
|
||||
Author = namedtuple("Author", ["account", "display_name"])
|
||||
|
||||
|
||||
def get_author(data, instance):
|
||||
# Show the author, not the persopn who reblogged
|
||||
status = data["reblog"] or data
|
||||
acct = status['account']['acct']
|
||||
acct = acct if "@" in acct else "{}@{}".format(acct, instance)
|
||||
return Author(acct, status['account']['display_name'])
|
||||
|
||||
|
||||
class Status:
|
||||
"""
|
||||
A wrapper around the Status entity data fetched from Mastodon.
|
||||
|
||||
https://github.com/tootsuite/documentation/blob/master/Using-the-API/API.md#status
|
||||
"""
|
||||
def __init__(self, data, instance):
|
||||
def __init__(self, data, is_mine, default_instance):
|
||||
self.data = data
|
||||
self.instance = instance
|
||||
self.is_mine = is_mine
|
||||
self.default_instance = default_instance
|
||||
|
||||
# This can be toggled by the user
|
||||
self.show_sensitive = False
|
||||
@ -33,14 +26,21 @@ class Status:
|
||||
self.display_name = self.data["account"]["display_name"]
|
||||
self.account = self.get_account()
|
||||
self.created_at = parse_datetime(data["created_at"])
|
||||
self.author = get_author(data, instance)
|
||||
self.author = self.get_author()
|
||||
self.favourited = data.get("favourited", False)
|
||||
self.reblogged = data.get("reblogged", False)
|
||||
self.in_reply_to = data.get("in_reply_to_id")
|
||||
|
||||
def get_author(self):
|
||||
# Show the author, not the persopn who reblogged
|
||||
data = self.data["reblog"] or self.data
|
||||
acct = data['account']['acct']
|
||||
acct = acct if "@" in acct else "{}@{}".format(acct, self.default_instance)
|
||||
return Author(acct, data['account']['display_name'])
|
||||
|
||||
def get_account(self):
|
||||
acct = self.data['account']['acct']
|
||||
return acct if "@" in acct else "{}@{}".format(acct, self.instance)
|
||||
return acct if "@" in acct else "{}@{}".format(acct, self.default_instance)
|
||||
|
||||
def __repr__(self):
|
||||
return "<Status id={}>".format(self.id)
|
||||
|
@ -30,6 +30,23 @@ class ExceptionStackTrace(urwid.ListBox):
|
||||
super().__init__(walker)
|
||||
|
||||
|
||||
class StatusDeleteConfirmation(urwid.ListBox):
|
||||
signals = ["delete", "close"]
|
||||
|
||||
def __init__(self, status):
|
||||
yes = SelectableText("Yes, send it to heck")
|
||||
no = SelectableText("No, I'll spare it for now")
|
||||
|
||||
urwid.connect_signal(yes, "click", lambda *args: self._emit("delete"))
|
||||
urwid.connect_signal(no, "click", lambda *args: self._emit("close"))
|
||||
|
||||
walker = urwid.SimpleFocusListWalker([
|
||||
urwid.AttrWrap(yes, "", "blue_selected"),
|
||||
urwid.AttrWrap(no, "", "blue_selected"),
|
||||
])
|
||||
super().__init__(walker)
|
||||
|
||||
|
||||
class GotoMenu(urwid.ListBox):
|
||||
signals = [
|
||||
"home_timeline",
|
||||
|
@ -17,6 +17,7 @@ class Timeline(urwid.Columns):
|
||||
signals = [
|
||||
"close", # Close thread
|
||||
"compose", # Compose a new toot
|
||||
"delete", # Delete own status
|
||||
"favourite", # Favourite status
|
||||
"focus", # Focus changed
|
||||
"media", # Display media attachments
|
||||
@ -110,6 +111,10 @@ class Timeline(urwid.Columns):
|
||||
self._emit("compose")
|
||||
return
|
||||
|
||||
if key in ("d", "D"):
|
||||
self._emit("delete", status)
|
||||
return
|
||||
|
||||
if key in ("f", "F"):
|
||||
self._emit("favourite", status)
|
||||
return
|
||||
@ -184,6 +189,14 @@ class Timeline(urwid.Columns):
|
||||
if index == self.status_list.body.focus:
|
||||
self.draw_status_details(status)
|
||||
|
||||
def remove_status(self, status):
|
||||
index = self.get_status_index(status.id)
|
||||
assert self.statuses[index].id == status.id # Sanity check
|
||||
|
||||
del(self.statuses[index])
|
||||
del(self.status_list.body[index])
|
||||
self.refresh_status_details()
|
||||
|
||||
|
||||
class StatusDetails(urwid.Pile):
|
||||
def __init__(self, status, in_thread):
|
||||
@ -247,8 +260,18 @@ class StatusDetails(urwid.Pile):
|
||||
# Push things to bottom
|
||||
yield ("weight", 1, urwid.SolidFill(" "))
|
||||
|
||||
options = "[B]oost [F]avourite [V]iew {}[R]eply So[u]rce [H]elp".format(
|
||||
"[T]hread " if not self.in_thread else "")
|
||||
options = [
|
||||
"[B]oost",
|
||||
"[D]elete" if status.is_mine else "",
|
||||
"[F]avourite",
|
||||
"[V]iew",
|
||||
"[T]hread" if not self.in_thread else "",
|
||||
"[R]eply",
|
||||
"So[u]rce",
|
||||
"[H]elp",
|
||||
]
|
||||
options = " ".join(o for o in options if o)
|
||||
|
||||
options = highlight_keys(options, "cyan_bold", "cyan")
|
||||
yield ("pack", urwid.Text(options))
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user