From 414a7fc47a4e8674a542330ecf5236e347455501 Mon Sep 17 00:00:00 2001 From: Ivan Habunek Date: Fri, 17 Feb 2023 09:29:07 +0100 Subject: [PATCH] wip --- toot/tui/app.py | 28 +++++++++++++++------------- toot/tui/entities.py | 9 +++++++-- toot/tui/timeline.py | 29 ++++++++++++++++------------- toot/tui/utils.py | 2 +- 4 files changed, 39 insertions(+), 29 deletions(-) diff --git a/toot/tui/app.py b/toot/tui/app.py index d61078c..fc9aa08 100644 --- a/toot/tui/app.py +++ b/toot/tui/app.py @@ -1,4 +1,5 @@ import logging +from typing import List import urwid from concurrent.futures import ThreadPoolExecutor @@ -9,7 +10,7 @@ from toot.exceptions import ApiError from .compose import StatusComposer from .constants import PALETTE -from .entities import Status, from_dict +from .entities import Status, StatusMeta, from_dict from .overlays import ExceptionStackTrace, GotoMenu, Help, StatusSource, StatusLinks, StatusZoom from .overlays import StatusDeleteConfirmation, Account from .timeline import Timeline @@ -182,7 +183,7 @@ class TUI(urwid.Frame): self.show_compose() def _delete(timeline, status): - if status.is_mine: + if status._meta.is_mine: self.show_delete_confirmation(status) def _reply(timeline, status): @@ -258,9 +259,9 @@ class TUI(urwid.Frame): return timeline def make_status(self, status_data): - return from_dict(Status, status_data) - # is_mine = self.user.username == status_data["account"]["acct"] - # return Status(status_data, is_mine, self.app.instance) + status = from_dict(Status, status_data) + status._meta = StatusMeta(status, self.app, self.user) + return status def show_thread(self, status): def _close(*args): @@ -307,10 +308,11 @@ class TUI(urwid.Frame): self.refresh_footer(self.timeline) self.body = self.timeline - def _done_next(statuses): + def _done_next(statuses: List[Status]): """Process sequential batch of statuses, adds statuses to the existing timeline.""" - self.timeline.append_statuses(statuses) + if self.timeline: + self.timeline.append_statuses(statuses) return self.run_in_thread(_load_statuses, done_callback=_done_initial if is_initial else _done_next) @@ -517,7 +519,7 @@ class TUI(urwid.Frame): title="Account", ) - def async_toggle_favourite(self, timeline, status): + def async_toggle_favourite(self, timeline: Timeline, status: Status): def _favourite(): logger.info("Favouriting {}".format(status)) api.favourite(self.app, self.user, status.id) @@ -555,7 +557,7 @@ class TUI(urwid.Frame): timeline.update_status(new_status) # Check if status is rebloggable - no_reblog_because_private = status.visibility == "private" and not status.is_mine + no_reblog_because_private = status.visibility == "private" and not status._meta.is_mine no_reblog_because_direct = status.visibility == "direct" if no_reblog_because_private or no_reblog_because_direct: self.footer.set_error_message("You may not reblog this {} status".format(status.visibility)) @@ -587,9 +589,9 @@ class TUI(urwid.Frame): def _done(response): if response is not None: - status.translation = response["content"] - status.translated_from = response["detected_source_language"] - status.show_translation = True + status._meta.translation = response["content"] + status._meta.translated_from = response["detected_source_language"] + status._meta.show_translation = True timeline.update_status(status) # If already translated, toggle showing translation @@ -599,7 +601,7 @@ class TUI(urwid.Frame): else: self.run_in_thread(_translate, done_callback=_done) - def async_toggle_bookmark(self, timeline, status): + def async_toggle_bookmark(self, timeline: Timeline, status: Status): def _bookmark(): logger.info("Bookmarking {}".format(status)) api.bookmark(self.app, self.user, status.id) diff --git a/toot/tui/entities.py b/toot/tui/entities.py index 5f267df..ec39d80 100644 --- a/toot/tui/entities.py +++ b/toot/tui/entities.py @@ -8,6 +8,7 @@ from dataclasses import dataclass, is_dataclass from datetime import date, datetime from typing import Dict, List, Optional, Type, TypeVar, Union from typing import get_type_hints +from toot import App, User from toot.typing_compat import get_args, get_origin from toot.utils import get_text @@ -190,16 +191,19 @@ class FilterResult: status_matches: Optional[str] -@dataclass class StatusMeta: """ Additional information kept for a status. """ + is_mine: bool show_sensitive: bool = False show_translation: bool = False translated_from: Optional[str] = None translation: Optional[str] = None + def __init__(self, status: "Status", app: App, user: User): + self.is_mine = user.username == status.account.acct + @dataclass class Status: @@ -238,7 +242,8 @@ class Status: pinned: Optional[bool] filtered: Optional[List[FilterResult]] - _meta: StatusMeta = dataclasses.field(default_factory=StatusMeta) + # Added by TUI for storing some context + _meta: Optional[StatusMeta] = None @property def original(self) -> "Status": diff --git a/toot/tui/timeline.py b/toot/tui/timeline.py index 6bc4a91..3681660 100644 --- a/toot/tui/timeline.py +++ b/toot/tui/timeline.py @@ -102,10 +102,13 @@ class Timeline(urwid.Columns): if not status: return None + assert status._meta + is_mine = status._meta.is_mine + options = [ - "[A]ccount" if not status.is_mine else "", + "[A]ccount" if not is_mine else "", "[B]oost", - "[D]elete" if status.is_mine else "", + "[D]elete" if is_mine else "", "B[o]okmark", "[F]avourite", "[V]iew", @@ -303,11 +306,15 @@ class StatusDetails(urwid.Pile): self.followed_tags = timeline.followed_tags reblogged_by = status.account if status and status.reblog else None - widget_list = list(self.content_generator(status.original, reblogged_by) + widget_list = list(self.content_generator(status, reblogged_by) if status else ()) return super().__init__(widget_list) def content_generator(self, status: Status, reblogged_by: Optional[Account]): + assert status._meta + meta = status._meta + status = status.original + if reblogged_by: text = "♺ {} boosted".format(reblogged_by.display_name or reblogged_by.username) yield ("pack", urwid.Text(("gray", text))) @@ -323,8 +330,6 @@ class StatusDetails(urwid.Pile): yield ("pack", urwid.Text(status.spoiler_text)) yield ("pack", urwid.Divider()) - meta = status._meta - # Show content warning if status.spoiler_text and not meta.show_sensitive: yield ("pack", urwid.Text(("content_warning", "Marked as sensitive. Press S to view."))) @@ -333,14 +338,12 @@ class StatusDetails(urwid.Pile): for line in format_content(content): yield ("pack", urwid.Text(highlight_hashtags(line, self.followed_tags))) - media = status.media_attachments - if media: - for m in media: - yield ("pack", urwid.AttrMap(urwid.Divider("-"), "gray")) - yield ("pack", urwid.Text([("bold", "Media attachment"), " (", m["type"], ")"])) - if m["description"]: - yield ("pack", urwid.Text(m["description"])) - yield ("pack", urwid.Text(("link", m["url"]))) + for m in status.media_attachments: + yield ("pack", urwid.AttrMap(urwid.Divider("-"), "gray")) + yield ("pack", urwid.Text([("bold", "Media attachment"), f" ({m.type})"])) + if m.description: + yield ("pack", urwid.Text(m.description)) + yield ("pack", urwid.Text(("link", m.url))) if status.poll: yield ("pack", urwid.Divider()) diff --git a/toot/tui/utils.py b/toot/tui/utils.py index e2855c4..8538a33 100644 --- a/toot/tui/utils.py +++ b/toot/tui/utils.py @@ -33,7 +33,7 @@ def parse_datetime(value): return dttm.astimezone() -def time_ago(value: datetime) -> datetime: +def time_ago(value: datetime) -> str: now = datetime.now().astimezone() delta = now.timestamp() - value.timestamp()