From b50fb9d9fddafb8809a39fc1c15a962fa7ae9551 Mon Sep 17 00:00:00 2001 From: Daniel Schwarz Date: Fri, 10 Feb 2023 21:44:37 -0500 Subject: [PATCH 01/13] Added vote method to api --- toot/api.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/toot/api.py b/toot/api.py index baa4c28..89c801b 100644 --- a/toot/api.py +++ b/toot/api.py @@ -361,6 +361,12 @@ def whois(app, user, account): return http.get(app, user, f'/api/v1/accounts/{account}').json() +def vote(app, user, poll_id, choices: list): + url = f"/api/v1/polls/{poll_id}/votes" + json = {'choices': choices} + return http.post(app, user, url, json=json).json() + + def mute(app, user, account): return _account_action(app, user, account, 'mute') From 44c8460a53b01fdf8fb8a2efb2c6c1817bb37c1b Mon Sep 17 00:00:00 2001 From: Daniel Schwarz Date: Fri, 10 Feb 2023 21:45:15 -0500 Subject: [PATCH 02/13] Poll view/vote functionality in an overlay --- toot/tui/poll.py | 98 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 toot/tui/poll.py 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) From c2faa7e2b6db0b2f8dbd5f0962b34ec9dde95708 Mon Sep 17 00:00:00 2001 From: Daniel Schwarz Date: Fri, 10 Feb 2023 21:46:27 -0500 Subject: [PATCH 03/13] Add signal and code to invoke poll overlay UI Note that this change also switches some key meanings: E -> X for View E[x]ception P -> E for Sav[e] Timeline to accomodate P for [P]oll --- toot/tui/app.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/toot/tui/app.py b/toot/tui/app.py index fbbf72a..e8c8218 100644 --- a/toot/tui/app.py +++ b/toot/tui/app.py @@ -12,6 +12,7 @@ from .constants import PALETTE from .entities import Status from .overlays import ExceptionStackTrace, GotoMenu, Help, StatusSource, StatusLinks, StatusZoom from .overlays import StatusDeleteConfirmation, Account +from .poll import Poll from .timeline import Timeline from .utils import parse_content_links, show_media @@ -155,7 +156,7 @@ class TUI(urwid.Frame): def _default_error_callback(ex): self.exception = ex - self.footer.set_error_message("An exception occurred, press E to view") + self.footer.set_error_message("An exception occurred, press X to view") _error_callback = error_callback or _default_error_callback @@ -200,6 +201,9 @@ class TUI(urwid.Frame): def _menu(timeline, status): self.show_context_menu(status) + def _poll(timeline, status): + self.show_poll(status) + def _zoom(timeline, status_details): self.show_status_zoom(status_details) @@ -214,6 +218,7 @@ class TUI(urwid.Frame): urwid.connect_signal(timeline, "focus", self.refresh_footer) urwid.connect_signal(timeline, "media", _media) urwid.connect_signal(timeline, "menu", _menu) + urwid.connect_signal(timeline, "poll", _poll) urwid.connect_signal(timeline, "reblog", self.async_toggle_reblog) urwid.connect_signal(timeline, "reply", _reply) urwid.connect_signal(timeline, "source", _source) @@ -445,6 +450,12 @@ class TUI(urwid.Frame): def show_help(self): self.open_overlay(Help(), title="Help") + def show_poll(self, status): + self.open_overlay( + widget=Poll(self.app, self.user, status), + title="Poll", + ) + def goto_home_timeline(self): self.timeline_generator = api.home_timeline_generator( self.app, self.user, limit=40) @@ -656,7 +667,7 @@ class TUI(urwid.Frame): def unhandled_input(self, key): # TODO: this should not be in unhandled input - if key in ('e', 'E'): + if key in ('x', 'X'): if self.exception: self.show_exception(self.exception) From d399eec6f5b8f9ef46cdecdac03f717e40d0dedc Mon Sep 17 00:00:00 2001 From: Daniel Schwarz Date: Fri, 10 Feb 2023 21:48:42 -0500 Subject: [PATCH 04/13] Implement [P]oll command to view/vote on poll Note that this change also switches some key meanings: E -> X for View E[x]ception P -> E for Sav[e] Timeline to accomodate P for [P]oll --- toot/tui/timeline.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/toot/tui/timeline.py b/toot/tui/timeline.py index 9059f5e..cfa88b8 100644 --- a/toot/tui/timeline.py +++ b/toot/tui/timeline.py @@ -31,6 +31,7 @@ class Timeline(urwid.Columns): "media", # Display media attachments "menu", # Show a context menu "next", # Fetch more statuses + "poll", # Vote in a poll "reblog", # Reblog status "reply", # Compose a reply to a status "source", # Show status source @@ -102,6 +103,8 @@ class Timeline(urwid.Columns): if not status: return None + poll = status.data.get("poll") + options = [ "[A]ccount" if not status.is_mine else "", "[B]oost", @@ -112,6 +115,7 @@ class Timeline(urwid.Columns): "[T]hread" if not self.is_thread else "", "[L]inks", "[R]eply", + "[P]oll" if poll and not poll["expired"] else "", "So[u]rce", "[Z]oom", "Tra[n]slate" if self.can_translate else "", @@ -240,7 +244,7 @@ class Timeline(urwid.Columns): self._emit("clear-screen") return - if key in ("p", "P"): + if key in ("e", "E"): self._emit("save", status) return @@ -248,6 +252,12 @@ class Timeline(urwid.Columns): self._emit("zoom", self.status_details) return + if key in ("p", "P"): + poll = status.data.get("poll") + if poll and not poll["expired"]: + self._emit("poll", status) + return + return super().keypress(size, key) def append_status(self, status): From 3b67c85dfc945802f8d62eaf163e084f420a966c Mon Sep 17 00:00:00 2001 From: Daniel Schwarz Date: Tue, 14 Feb 2023 22:21:04 -0500 Subject: [PATCH 05/13] Added styled radio buttons and checkboxes --- toot/tui/poll.py | 8 ++++---- toot/tui/widgets.py | 20 ++++++++++++++++++++ 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/toot/tui/poll.py b/toot/tui/poll.py index fc6f68a..dc24046 100644 --- a/toot/tui/poll.py +++ b/toot/tui/poll.py @@ -4,7 +4,7 @@ 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 +from .widgets import Button, CheckBox, RadioButton class Poll(urwid.ListBox): @@ -33,7 +33,7 @@ class Poll(urwid.ListBox): poll = self.status.data.get("poll") choices = [] for idx, b in enumerate(self.button_group): - if b.state: + if b.get_state(): choices.append(idx) if len(choices): @@ -62,11 +62,11 @@ class Poll(urwid.ListBox): yield urwid.Text(("gray", prefix + f'{option["title"]}')) else: if poll["multiple"]: - cb = urwid.CheckBox(f'{option["title"]}') + cb = CheckBox(f'{option["title"]}') self.button_group.append(cb) yield cb else: - yield urwid.RadioButton(self.button_group, f'{option["title"]}') + yield RadioButton(self.button_group, f'{option["title"]}') yield urwid.Divider() diff --git a/toot/tui/widgets.py b/toot/tui/widgets.py index a311d52..6f46fb3 100644 --- a/toot/tui/widgets.py +++ b/toot/tui/widgets.py @@ -46,3 +46,23 @@ class Button(urwid.AttrWrap): def set_label(self, *args, **kwargs): self.original_widget.original_widget.set_label(*args, **kwargs) self.original_widget.width = len(args[0]) + 4 + + +class CheckBox(urwid.AttrWrap): + """Styled checkbox.""" + def __init__(self, *args, **kwargs): + self.button = urwid.CheckBox(*args, **kwargs) + padding = urwid.Padding(self.button, width=len(args[0]) + 4) + return super().__init__(padding, "button", "button_focused") + + def get_state(self): + """Return the state of the checkbox.""" + return self.button._state + + +class RadioButton(urwid.AttrWrap): + """Styled radiobutton.""" + def __init__(self, *args, **kwargs): + button = urwid.RadioButton(*args, **kwargs) + padding = urwid.Padding(button, width=len(args[1]) + 4) + return super().__init__(padding, "button", "button_focused") From ef2c35eaeeeb448aad70d759ec030fc344edd8f6 Mon Sep 17 00:00:00 2001 From: Daniel Schwarz Date: Tue, 14 Feb 2023 22:21:04 -0500 Subject: [PATCH 06/13] Removed unneeded pareenthesis --- toot/tui/utils.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/toot/tui/utils.py b/toot/tui/utils.py index e2855c4..cfc0778 100644 --- a/toot/tui/utils.py +++ b/toot/tui/utils.py @@ -37,19 +37,19 @@ def time_ago(value: datetime) -> datetime: now = datetime.now().astimezone() delta = now.timestamp() - value.timestamp() - if (delta < 1): + if delta < 1: return "now" - if (delta < 8 * DAY): - if (delta < MINUTE): + if delta < 8 * DAY: + if delta < MINUTE: return f"{math.floor(delta / SECOND)}".rjust(2, " ") + "s" - if (delta < HOUR): + if delta < HOUR: return f"{math.floor(delta / MINUTE)}".rjust(2, " ") + "m" - if (delta < DAY): + if delta < DAY: return f"{math.floor(delta / HOUR)}".rjust(2, " ") + "h" return f"{math.floor(delta / DAY)}".rjust(2, " ") + "d" - if (delta < 53 * WEEK): # not exactly correct but good enough as a boundary + if delta < 53 * WEEK: # not exactly correct but good enough as a boundary return f"{math.floor(delta / WEEK)}".rjust(2, " ") + "w" return ">1y" From 41a8c334092441a792d534835925e597b870923c Mon Sep 17 00:00:00 2001 From: Daniel Schwarz Date: Tue, 14 Feb 2023 22:21:04 -0500 Subject: [PATCH 07/13] Removed unneeded parenthesis --- toot/tui/overlays.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/toot/tui/overlays.py b/toot/tui/overlays.py index 96680aa..de9dbe6 100644 --- a/toot/tui/overlays.py +++ b/toot/tui/overlays.py @@ -211,7 +211,7 @@ class Account(urwid.ListBox): super().__init__(walker) def generate_contents(self, account): - yield urwid.Text([('green', f"@{account['acct']}"), (f" {account['display_name']}")]) + yield urwid.Text([('green', f"@{account['acct']}"), f" {account['display_name']}"]) if account["note"]: yield urwid.Divider() @@ -219,8 +219,8 @@ class Account(urwid.ListBox): yield urwid.Text(highlight_hashtags(line, followed_tags=set())) yield urwid.Divider() - yield urwid.Text([("ID: "), ("green", f"{account['id']}")]) - yield urwid.Text([("Since: "), ("green", f"{account['created_at'][:10]}")]) + yield urwid.Text(["ID: ", ("green", f"{account['id']}")]) + yield urwid.Text(["Since: ", ("green", f"{account['created_at'][:10]}")]) yield urwid.Divider() if account["bot"]: @@ -233,15 +233,15 @@ class Account(urwid.ListBox): yield urwid.Text([("warning", "Suspended \N{cross mark}")]) yield urwid.Divider() - yield urwid.Text([("Followers: "), ("yellow", f"{account['followers_count']}")]) - yield urwid.Text([("Following: "), ("yellow", f"{account['following_count']}")]) - yield urwid.Text([("Statuses: "), ("yellow", f"{account['statuses_count']}")]) + yield urwid.Text(["Followers: ", ("yellow", f"{account['followers_count']}")]) + yield urwid.Text(["Following: ", ("yellow", f"{account['following_count']}")]) + yield urwid.Text(["Statuses: ", ("yellow", f"{account['statuses_count']}")]) if account["fields"]: for field in account["fields"]: name = field["name"].title() yield urwid.Divider() - yield urwid.Text([("yellow", f"{name.rstrip(':')}"), (":")]) + yield urwid.Text([("yellow", f"{name.rstrip(':')}"), ":"]) for line in format_content(field["value"]): yield urwid.Text(highlight_hashtags(line, followed_tags=set())) if field["verified_at"]: From ce34a2f05ee85e0454d36336fc92384baf0f3a92 Mon Sep 17 00:00:00 2001 From: Daniel Schwarz Date: Tue, 14 Feb 2023 22:21:04 -0500 Subject: [PATCH 08/13] Added api error message reporting, removed unnecessary parenthesis --- toot/tui/poll.py | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/toot/tui/poll.py b/toot/tui/poll.py index dc24046..c2f0dcb 100644 --- a/toot/tui/poll.py +++ b/toot/tui/poll.py @@ -16,7 +16,7 @@ class Poll(urwid.ListBox): self.user = user self.poll = status.data.get("poll") self.button_group = [] - + self.api_exception = None self.setup_listbox() def setup_listbox(self): @@ -29,25 +29,26 @@ class Poll(urwid.ListBox): contents = urwid.Padding(contents, left=1, right=1) return urwid.LineBox(contents) - def vote(self, button): + def vote(self, button_widget): poll = self.status.data.get("poll") choices = [] - for idx, b in enumerate(self.button_group): - if b.get_state(): + for idx, button in enumerate(self.button_group): + if button.get_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.api_exception = None self.poll["voted"] = True self.poll["own_votes"] = choices - except ApiError: - pass # FIXME error reporting? + except ApiError as exception: + self.api_exception = exception finally: self.setup_listbox() - def generate_poll_detail(self, status): + def generate_poll_detail(self): poll = self.poll if poll: @@ -62,9 +63,9 @@ class Poll(urwid.ListBox): yield urwid.Text(("gray", prefix + f'{option["title"]}')) else: if poll["multiple"]: - cb = CheckBox(f'{option["title"]}') - self.button_group.append(cb) - yield cb + checkbox = CheckBox(f'{option["title"]}') + self.button_group.append(checkbox) + yield checkbox else: yield RadioButton(self.button_group, f'{option["title"]}') @@ -86,13 +87,17 @@ class Poll(urwid.ListBox): 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.Text(highlight_hashtags(line, set())) yield urwid.Divider() - yield (self.build_linebox(self.generate_poll_detail(status))) + yield self.build_linebox(self.generate_poll_detail()) 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) + + if self.api_exception: + yield urwid.Divider() + yield urwid.Text("warning", str(self.api_exception)) From b2a0bc563458555ddd37a8b2ce7d2ec8416eeded Mon Sep 17 00:00:00 2001 From: Daniel Schwarz Date: Tue, 14 Feb 2023 22:40:06 -0500 Subject: [PATCH 09/13] fixed method signature for vote (added list type of int) --- toot/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/toot/api.py b/toot/api.py index 89c801b..9d46e23 100644 --- a/toot/api.py +++ b/toot/api.py @@ -361,7 +361,7 @@ def whois(app, user, account): return http.get(app, user, f'/api/v1/accounts/{account}').json() -def vote(app, user, poll_id, choices: list): +def vote(app, user, poll_id, choices: list[int]): url = f"/api/v1/polls/{poll_id}/votes" json = {'choices': choices} return http.post(app, user, url, json=json).json() From 63bc11316b64ed20e7d058b0ffcc4c4a3c21ffcb Mon Sep 17 00:00:00 2001 From: Daniel Schwarz Date: Tue, 14 Feb 2023 22:40:35 -0500 Subject: [PATCH 10/13] Removed unnecessary check that poll exists --- toot/tui/poll.py | 49 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/toot/tui/poll.py b/toot/tui/poll.py index c2f0dcb..f05042c 100644 --- a/toot/tui/poll.py +++ b/toot/tui/poll.py @@ -51,38 +51,37 @@ class Poll(urwid.ListBox): def generate_poll_detail(self): 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"] - ) + 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"]}')) + if poll["voted"] or poll["expired"]: + prefix = " ✓ " if voted_for else " " + yield urwid.Text(("gray", prefix + f'{option["title"]}')) + else: + if poll["multiple"]: + checkbox = CheckBox(f'{option["title"]}') + self.button_group.append(checkbox) + yield checkbox else: - if poll["multiple"]: - checkbox = CheckBox(f'{option["title"]}') - self.button_group.append(checkbox) - yield checkbox - else: - yield RadioButton(self.button_group, f'{option["title"]}') + yield RadioButton(self.button_group, f'{option["title"]}') - yield urwid.Divider() + yield urwid.Divider() - poll_detail = "Poll · {} votes".format(poll["votes_count"]) + poll_detail = "Poll · {} votes".format(poll["votes_count"]) - if poll["expired"]: - poll_detail += " · Closed" + 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) + 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)) + yield urwid.Text(("gray", poll_detail)) def generate_contents(self, status): yield urwid.Divider() From a937650894fce5b3b880cfddacc3ea63eb9e5192 Mon Sep 17 00:00:00 2001 From: Daniel Schwarz Date: Tue, 14 Feb 2023 23:11:11 -0500 Subject: [PATCH 11/13] Update status detail after leaving overlay This is currently used for poll voting; after voting in a poll overlay, the status detail will update with the user's vote and the new vote count when they dismiss the overlay. As part of this change, the refresh_status_detail method now maintains the scroll position after refresh, rather than scrolling back to the top automatically --- toot/tui/app.py | 2 ++ toot/tui/timeline.py | 7 +++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/toot/tui/app.py b/toot/tui/app.py index e8c8218..64c50c7 100644 --- a/toot/tui/app.py +++ b/toot/tui/app.py @@ -662,6 +662,8 @@ class TUI(urwid.Frame): def close_overlay(self): self.body = self.overlay.bottom_w self.overlay = None + if self.timeline: + self.timeline.refresh_status_details() # --- Keys ----------------------------------------------------------------- diff --git a/toot/tui/timeline.py b/toot/tui/timeline.py index cfa88b8..b8c5c20 100644 --- a/toot/tui/timeline.py +++ b/toot/tui/timeline.py @@ -66,11 +66,12 @@ class Timeline(urwid.Columns): ]) def wrap_status_details(self, status_details: "StatusDetails") -> urwid.Widget: - """Wrap StatusDetails widget with a scollbar and footer.""" + """Wrap StatusDetails widget with a scrollbar and footer.""" + self.status_detail_scrollable = Scrollable(urwid.Padding(status_details, right=1)) return urwid.Padding( urwid.Frame( body=ScrollBar( - Scrollable(urwid.Padding(status_details, right=1)), + self.status_detail_scrollable, thumb_char="\u2588", trough_char="\u2591", ), @@ -152,7 +153,9 @@ class Timeline(urwid.Columns): def refresh_status_details(self): """Redraws the details of the focused status.""" status = self.get_focused_status() + pos = self.status_detail_scrollable.get_scrollpos() self.draw_status_details(status) + self.status_detail_scrollable.set_scrollpos(pos) def draw_status_details(self, status): self.status_details = StatusDetails(self, status) From 68cadd405362bc1b37a74e976b6491db815c4555 Mon Sep 17 00:00:00 2001 From: Daniel Schwarz Date: Wed, 15 Feb 2023 10:14:41 -0500 Subject: [PATCH 12/13] Boosted polls weren't working; this change fixes that. --- toot/tui/poll.py | 6 +++--- toot/tui/timeline.py | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/toot/tui/poll.py b/toot/tui/poll.py index f05042c..81756af 100644 --- a/toot/tui/poll.py +++ b/toot/tui/poll.py @@ -14,7 +14,7 @@ class Poll(urwid.ListBox): self.status = status self.app = app self.user = user - self.poll = status.data.get("poll") + self.poll = status.original.data.get("poll") self.button_group = [] self.api_exception = None self.setup_listbox() @@ -30,7 +30,7 @@ class Poll(urwid.ListBox): return urwid.LineBox(contents) def vote(self, button_widget): - poll = self.status.data.get("poll") + poll = self.status.original.data.get("poll") choices = [] for idx, button in enumerate(self.button_group): if button.get_state(): @@ -39,7 +39,7 @@ class Poll(urwid.ListBox): if len(choices): try: response = api.vote(self.app, self.user, poll["id"], choices=choices) - self.status.data["poll"] = response + self.status.original.data["poll"] = response self.api_exception = None self.poll["voted"] = True self.poll["own_votes"] = choices diff --git a/toot/tui/timeline.py b/toot/tui/timeline.py index b8c5c20..555d0c5 100644 --- a/toot/tui/timeline.py +++ b/toot/tui/timeline.py @@ -104,7 +104,7 @@ class Timeline(urwid.Columns): if not status: return None - poll = status.data.get("poll") + poll = status.original.data.get("poll") options = [ "[A]ccount" if not status.is_mine else "", @@ -256,7 +256,7 @@ class Timeline(urwid.Columns): return if key in ("p", "P"): - poll = status.data.get("poll") + poll = status.original.data.get("poll") if poll and not poll["expired"]: self._emit("poll", status) return @@ -353,7 +353,7 @@ class StatusDetails(urwid.Pile): yield ("pack", urwid.Text(m["description"])) yield ("pack", urwid.Text(("link", m["url"]))) - poll = status.data.get("poll") + poll = status.original.data.get("poll") if poll: yield ("pack", urwid.Divider()) yield ("pack", self.build_linebox(self.poll_generator(poll))) From 80e711a3a1ef9c1b6d7ff045b2e1701e910b0517 Mon Sep 17 00:00:00 2001 From: Daniel Schwarz Date: Wed, 15 Feb 2023 21:50:50 -0500 Subject: [PATCH 13/13] Fixed type error in vote method signature --- toot/api.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/toot/api.py b/toot/api.py index 9d46e23..795492b 100644 --- a/toot/api.py +++ b/toot/api.py @@ -1,4 +1,5 @@ import re +from typing import List import uuid from urllib.parse import urlparse, urlencode, quote @@ -361,7 +362,7 @@ def whois(app, user, account): return http.get(app, user, f'/api/v1/accounts/{account}').json() -def vote(app, user, poll_id, choices: list[int]): +def vote(app, user, poll_id, choices: List[int]): url = f"/api/v1/polls/{poll_id}/votes" json = {'choices': choices} return http.post(app, user, url, json=json).json()