1
0
mirror of https://github.com/ihabunek/toot.git synced 2024-06-16 06:15:25 +00:00

Implement posting statuses

This commit is contained in:
Ivan Habunek 2019-08-27 10:02:13 +02:00
parent b95aca964f
commit 366e9382d3
No known key found for this signature in database
GPG Key ID: CDBD63C43A30BB95
5 changed files with 165 additions and 21 deletions

View File

@ -1,16 +1,24 @@
maybe ???
https://github.com/CanonicalLtd/subiquity/blob/master/subiquitycore/core.py#L280
educational:
https://github.com/TomasTomecek/sen/blob/master/sen/tui/ui.py
check out:
https://github.com/rndusr/stig/tree/master/stig/tui
Interesting urwid implementations:
* https://github.com/CanonicalLtd/subiquity/blob/master/subiquitycore/core.py#L280
* https://github.com/TomasTomecek/sen/blob/master/sen/tui/ui.py
* https://github.com/rndusr/stig/tree/master/stig/tui
TODO/Ideas:
* pack left column in timeline view
* when an error happens, show it in the status bar and have "press E to view exception" to show it in an overlay.
* maybe even have error reporting? e.g. button to open an issue on github?
* allow scrolling of toot contents if they don't fit the screen, perhaps using
pageup/pagedown
* consider adding semi-automated error reporting when viewing an exception,
something along the lines of "press T to submit a ticket", which would link
to a pre-filled issue submit page.
* show new toots, some ideas:
* R to reload/refresh timeline
* streaming new toots? not sold on the idea
* go up on first toot to fetch any newer ones, and prepend them?
* Switch timeline to top/bottom layout for narrow views.
* Think about how to show media
* download media and use local image viewer?
* convert to ascii art?
* use signals to avoid tightly coupling components
Questions:
* is it possible to make a span a urwid.Text selectable? e.g. for urls and hashtags

View File

@ -7,6 +7,7 @@ from concurrent.futures import ThreadPoolExecutor
from toot import api, __version__
from .compose import StatusComposer
from .constants import PALETTE
from .entities import Status
from .timeline import Timeline
@ -225,6 +226,30 @@ class TUI(urwid.Frame):
},
)
def show_compose(self):
composer = StatusComposer()
urwid.connect_signal(composer, "close",
lambda *args: self.close_overlay())
urwid.connect_signal(composer, "post",
lambda _, content, warning: self.post_status(content, warning))
self.open_overlay(
widget=composer,
title="Compose status",
options={
"align": 'center',
"width": ('relative', 80),
"valign": 'middle',
"height": ('relative', 80),
},
)
def post_status(self, content, warning):
data = api.post_status(self.app, self.user, content, spoiler_text=warning)
status = Status(data, self.app.instance)
self.timeline.prepend_status(status)
self.footer.set_message("Status posted {} \\o/".format(status.id))
self.close_overlay()
def async_toggle_favourite(self, status):
def _favourite():
logger.info("Favouriting {}".format(status))
@ -285,11 +310,16 @@ class TUI(urwid.Frame):
# --- Keys -----------------------------------------------------------------
def unhandled_input(self, key):
# TODO: this should not be in unhandled input
if key in ('e', 'E'):
if self.exception:
self.show_exception(self.exception)
if key in ('q', 'Q'):
elif key == 'esc':
if self.overlay:
self.close_overlay()
elif key in ('q', 'Q'):
if self.overlay:
self.close_overlay()
else:

87
toot/tui/compose.py Normal file
View File

@ -0,0 +1,87 @@
import urwid
import logging
logger = logging.getLogger(__name__)
class EditBox(urwid.AttrWrap):
def __init__(self):
edit = urwid.Edit(multiline=True, allow_tab=True)
return super().__init__(edit, "editbox", "editbox_focused")
class Button(urwid.AttrWrap):
def __init__(self, *args, **kwargs):
button = urwid.Button(*args, **kwargs)
padding = urwid.Padding(button, width=len(args[0]) + 4)
return super().__init__(padding, "button", "button_focused")
def set_label(self, *args, **kwargs):
self.original_widget.original_widget.set_label(*args, **kwargs)
self.original_widget.width = len(args[0]) + 4
class StatusComposer(urwid.Frame):
signals = ["close", "post"]
def __init__(self):
# This can be added by button press
self.content = EditBox()
self.content_warning = None
self.cw_button = Button("Add content warning", on_press=self.toggle_cw)
contents = [
urwid.Text("Status message"),
self.content,
urwid.Divider(),
self.cw_button,
Button("Post", on_press=self.post),
Button("Cancel", on_press=self.close),
]
self.walker = urwid.SimpleListWalker(contents)
self.listbox = urwid.ListBox(self.walker)
return super().__init__(self.listbox)
def toggle_cw(self, button):
if self.content_warning:
self.cw_button.set_label("Add content warning")
self.walker.pop(2)
self.walker.pop(2)
self.walker.pop(2)
self.walker.set_focus(3)
self.content_warning = None
else:
self.cw_button.set_label("Remove content warning")
self.content_warning = EditBox()
self.walker.insert(2, self.content_warning)
self.walker.insert(2, urwid.Text("Content warning"))
self.walker.insert(2, urwid.Divider())
self.walker.set_focus(4)
def clear_error_message(self):
self.footer = None
def set_error_message(self, msg):
self.footer = urwid.Text(("footer_message_error", msg))
def post(self, button):
self.clear_error_message()
# Don't lstrip content to avoid removing intentional leading whitespace
# However, do strip both sides to check if there is any content there
content = self.content.edit_text.rstrip()
content = None if not content.strip() else content
warning = (self.content_warning.edit_text.rstrip()
if self.content_warning else "")
warning = None if not warning.strip() else warning
if not content:
self.set_error_message("Cannot post an empty message")
return
self._emit("post", content, warning)
def close(self, button):
self._emit("close")

View File

@ -1,8 +1,12 @@
# name, fg, bg, mono, fg_h, bg_h
PALETTE = [
# Components
('button', 'white', 'black'),
('button_focused', 'light gray', 'dark magenta'),
('editbox', 'white', 'black'),
('editbox_focused', '', 'dark magenta'),
('footer_message', 'dark green', ''),
('footer_message_error', 'white', 'dark red'),
('footer_message_error', 'light red', ''),
('footer_status', 'white', 'dark blue'),
('footer_status_bold', 'white, bold', 'dark blue'),
('header', 'white', 'dark blue'),

View File

@ -2,7 +2,6 @@ import logging
import urwid
import webbrowser
from toot import api
from toot.utils import format_content
from .utils import highlight_hashtags
@ -14,9 +13,6 @@ logger = logging.getLogger("toot")
class Timeline(urwid.Columns):
"""
Displays a list of statuses to the left, and status details on the right.
TODO: Switch to top/bottom for narrow views.
TODO: Cache rendered statuses?
"""
signals = [
"status_focused",
@ -68,13 +64,16 @@ class Timeline(urwid.Columns):
def status_focused(self):
"""Called when the list focus switches to a new status"""
status = self.get_focused_status()
self.status_details = StatusDetails(status)
self.contents[1] = self.status_details, ("weight", 50, False)
self.draw_status_details(status)
index = self.status_list.body.focus
count = len(self.statuses)
self._emit("status_focused", [status, index, count])
def draw_status_details(self, status):
self.status_details = StatusDetails(status)
self.contents[1] = self.status_details, ("weight", 50, False)
def keypress(self, size, key):
# If down is pressed on last status in list emit a signal to load more.
# TODO: Consider pre-loading statuses earlier
@ -90,6 +89,10 @@ class Timeline(urwid.Columns):
self.tui.async_toggle_reblog(status)
return
if key in ('c', 'C'):
self.tui.show_compose()
return
if key in ("f", "F"):
status = self.get_focused_status()
self.tui.async_toggle_favourite(status)
@ -112,6 +115,17 @@ class Timeline(urwid.Columns):
self.status_index_map[status.id] = len(self.statuses) - 1
self.status_list.body.append(self.build_list_item(status))
def prepend_status(self, status):
self.statuses.insert(0, status)
# Need to rebuild the map, there has to be a better way
self.status_index_map = {
status.id: n for n, status in enumerate(self.statuses)
}
self.status_list.body.insert(0, self.build_list_item(status))
if self.status_list.body.focus == 0:
self.draw_status_details(status)
def add_statuses(self, statuses):
for status in statuses:
self.add_status(status)
@ -129,8 +143,8 @@ class Timeline(urwid.Columns):
# Redraw status details if status is focused
if index == self.status_list.body.focus:
self.status_details = StatusDetails(status)
self.contents[1] = self.status_details, ("weight", 50, False)
self.draw_status_details(status)
class StatusDetails(urwid.Pile):
def __init__(self, status):
@ -161,7 +175,8 @@ class StatusDetails(urwid.Pile):
yield ("pack", urwid.Text([
("cyan_bold", "B"), ("cyan", "oost"), " | ",
("cyan_bold", "F"), ("cyan", "avourite"), " | ",
("cyan_bold", "V"), ("cyan", "iew in browser"), " | ",
("cyan_bold", "V"), ("cyan", "iew"), " | ",
("cyan", "So"), ("cyan_bold", "u"), ("cyan", "rce"), " | ",
("cyan_bold", "H"), ("cyan", "elp"), " ",
]))