diff --git a/toot/tui/poll.py b/toot/tui/poll.py new file mode 100644 index 0000000..fc6f68a --- /dev/null +++ b/toot/tui/poll.py @@ -0,0 +1,98 @@ +import urwid + +from toot import api +from toot.exceptions import ApiError +from toot.utils import format_content +from .utils import highlight_hashtags, parse_datetime +from .widgets import Button + + +class Poll(urwid.ListBox): + """View and vote on a poll""" + + def __init__(self, app, user, status): + self.status = status + self.app = app + self.user = user + self.poll = status.data.get("poll") + self.button_group = [] + + self.setup_listbox() + + def setup_listbox(self): + actions = list(self.generate_contents(self.status)) + walker = urwid.SimpleListWalker(actions) + super().__init__(walker) + + def build_linebox(self, contents): + contents = urwid.Pile(list(contents)) + contents = urwid.Padding(contents, left=1, right=1) + return urwid.LineBox(contents) + + def vote(self, button): + poll = self.status.data.get("poll") + choices = [] + for idx, b in enumerate(self.button_group): + if b.state: + choices.append(idx) + + if len(choices): + try: + response = api.vote(self.app, self.user, poll["id"], choices=choices) + self.status.data["poll"] = response + self.poll["voted"] = True + self.poll["own_votes"] = choices + except ApiError: + pass # FIXME error reporting? + finally: + self.setup_listbox() + + def generate_poll_detail(self, status): + poll = self.poll + + if poll: + self.button_group = [] # button group + for idx, option in enumerate(poll["options"]): + voted_for = ( + poll["voted"] and poll["own_votes"] and idx in poll["own_votes"] + ) + + if poll["voted"] or poll["expired"]: + prefix = " ✓ " if voted_for else " " + yield urwid.Text(("gray", prefix + f'{option["title"]}')) + else: + if poll["multiple"]: + cb = urwid.CheckBox(f'{option["title"]}') + self.button_group.append(cb) + yield cb + else: + yield urwid.RadioButton(self.button_group, f'{option["title"]}') + + yield urwid.Divider() + + poll_detail = "Poll · {} votes".format(poll["votes_count"]) + + if poll["expired"]: + poll_detail += " · Closed" + + if poll["expires_at"]: + expires_at = parse_datetime(poll["expires_at"]).strftime( + "%Y-%m-%d %H:%M" + ) + poll_detail += " · Closes on {}".format(expires_at) + + yield urwid.Text(("gray", poll_detail)) + + def generate_contents(self, status): + yield urwid.Divider() + for line in format_content(status.data["content"]): + yield (urwid.Text(highlight_hashtags(line, set()))) + + yield urwid.Divider() + yield (self.build_linebox(self.generate_poll_detail(status))) + yield urwid.Divider() + + if self.poll["voted"]: + yield urwid.Text(("grey", "< Already Voted >")) + elif not self.poll["expired"]: + yield Button("Vote", on_press=self.vote)