Ivan Habunek 2023-02-15 08:54:45 +01:00
rodzic fd12591ee2
commit 4b40f45688
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: F5F0623FF5EBCB3D
3 zmienionych plików z 72 dodań i 63 usunięć

Wyświetl plik

@ -1,3 +1,7 @@
"""
Dataclasses which represent entities returned by the Mastodon API.
"""
import dataclasses import dataclasses
from dataclasses import dataclass, is_dataclass from dataclasses import dataclass, is_dataclass
@ -75,6 +79,9 @@ class Application:
@dataclass @dataclass
class MediaAttachment: class MediaAttachment:
"""
https://docs.joinmastodon.org/entities/MediaAttachment/
"""
id: str id: str
type: str type: str
url: str url: str
@ -133,6 +140,9 @@ class Poll:
@dataclass @dataclass
class PreviewCard: class PreviewCard:
"""
https://docs.joinmastodon.org/entities/PreviewCard/
"""
url: str url: str
title: str title: str
description: str description: str
@ -180,6 +190,17 @@ class FilterResult:
status_matches: Optional[str] status_matches: Optional[str]
@dataclass
class StatusMeta:
"""
Additional information kept for a status.
"""
show_sensitive: bool = False
show_translation: bool = False
translated_from: Optional[str] = None
translation: Optional[str] = None
@dataclass @dataclass
class Status: class Status:
id: str id: str
@ -214,27 +235,13 @@ class Status:
pinned: Optional[bool] pinned: Optional[bool]
filtered: Optional[List[FilterResult]] filtered: Optional[List[FilterResult]]
_meta: StatusMeta = dataclasses.field(default_factory=StatusMeta)
@property @property
def original(self) -> "Status": def original(self) -> "Status":
return self.reblog or self return self.reblog or self
@dataclass
class InstanceV2:
domain: str
title: str
version: str
source_url: str
description: str
usage: dict # TODO expand
thumbnail: dict # TODO expand
languages: List[str]
configuration: dict # TODO expand
registrations: dict # TODO expand
contact: dict # TODO expand
rules: List[dict] # TODO expand
# Generic data class instance # Generic data class instance
T = TypeVar("T") T = TypeVar("T")

Wyświetl plik

@ -4,6 +4,7 @@ import urwid
import webbrowser import webbrowser
from toot import __version__ from toot import __version__
from toot.tui.entities import Status
from toot.utils import format_content from toot.utils import format_content
from .utils import highlight_hashtags, highlight_keys from .utils import highlight_hashtags, highlight_keys
from .widgets import Button, EditBox, SelectableText from .widgets import Button, EditBox, SelectableText
@ -11,7 +12,7 @@ from .widgets import Button, EditBox, SelectableText
class StatusSource(urwid.Padding): class StatusSource(urwid.Padding):
"""Shows status data, as returned by the server, as formatted JSON.""" """Shows status data, as returned by the server, as formatted JSON."""
def __init__(self, status): def __init__(self, status: Status):
self.source = json.dumps(status.data, indent=4) self.source = json.dumps(status.data, indent=4)
self.filename_edit = EditBox(caption="Filename: ", edit_text=f"status-{status.id}.json") self.filename_edit = EditBox(caption="Filename: ", edit_text=f"status-{status.id}.json")
self.status_text = urwid.Text("") self.status_text = urwid.Text("")
@ -79,7 +80,7 @@ class ExceptionStackTrace(urwid.ListBox):
class StatusDeleteConfirmation(urwid.ListBox): class StatusDeleteConfirmation(urwid.ListBox):
signals = ["delete", "close"] signals = ["delete", "close"]
def __init__(self, status): def __init__(self, status: Status):
yes = SelectableText("Yes, send it to heck") yes = SelectableText("Yes, send it to heck")
no = SelectableText("No, I'll spare it for now") no = SelectableText("No, I'll spare it for now")

Wyświetl plik

@ -5,7 +5,7 @@ import webbrowser
from typing import Optional from typing import Optional
from .entities import Status from .entities import Account, Poll, PreviewCard, Status
from .scroll import Scrollable, ScrollBar from .scroll import Scrollable, ScrollBar
from .utils import highlight_hashtags, parse_datetime, highlight_keys from .utils import highlight_hashtags, parse_datetime, highlight_keys
from .widgets import SelectableText, SelectableColumns from .widgets import SelectableText, SelectableColumns
@ -302,36 +302,38 @@ class StatusDetails(urwid.Pile):
self.status = status self.status = status
self.followed_tags = timeline.followed_tags self.followed_tags = timeline.followed_tags
reblogged_by = status.author if status and status.reblog else None 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.original, reblogged_by)
if status else ()) if status else ())
return super().__init__(widget_list) return super().__init__(widget_list)
def content_generator(self, status, reblogged_by): def content_generator(self, status: Status, reblogged_by: Account):
meta = status._meta
if reblogged_by: if reblogged_by:
text = "{} boosted".format(reblogged_by.display_name or reblogged_by.username) text = "{} boosted".format(reblogged_by.display_name or reblogged_by.username)
yield ("pack", urwid.Text(("gray", text))) yield ("pack", urwid.Text(("gray", text)))
yield ("pack", urwid.AttrMap(urwid.Divider("-"), "gray")) yield ("pack", urwid.AttrMap(urwid.Divider("-"), "gray"))
if status.author.display_name: if status.account.display_name:
yield ("pack", urwid.Text(("green", status.author.display_name))) yield ("pack", urwid.Text(("green", status.account.display_name)))
yield ("pack", urwid.Text(("yellow", status.author.account))) yield ("pack", urwid.Text(("yellow", status.account.acct)))
yield ("pack", urwid.Divider()) yield ("pack", urwid.Divider())
if status.data["spoiler_text"]: if status.spoiler_text:
yield ("pack", urwid.Text(status.data["spoiler_text"])) yield ("pack", urwid.Text(status.spoiler_text))
yield ("pack", urwid.Divider()) yield ("pack", urwid.Divider())
# Show content warning # Show content warning
if status.data["spoiler_text"] and not status.show_sensitive: if status.spoiler_text and not meta.show_sensitive:
yield ("pack", urwid.Text(("content_warning", "Marked as sensitive. Press S to view."))) yield ("pack", urwid.Text(("content_warning", "Marked as sensitive. Press S to view.")))
else: else:
content = status.translation if status.show_translation else status.data["content"] content = meta.translation if meta.show_translation else status.content
for line in format_content(content): for line in format_content(content):
yield ("pack", urwid.Text(highlight_hashtags(line, self.followed_tags))) yield ("pack", urwid.Text(highlight_hashtags(line, self.followed_tags)))
media = status.data["media_attachments"] media = status.media_attachments
if media: if media:
for m in media: for m in media:
yield ("pack", urwid.AttrMap(urwid.Divider("-"), "gray")) yield ("pack", urwid.AttrMap(urwid.Divider("-"), "gray"))
@ -340,24 +342,21 @@ class StatusDetails(urwid.Pile):
yield ("pack", urwid.Text(m["description"])) yield ("pack", urwid.Text(m["description"]))
yield ("pack", urwid.Text(("link", m["url"]))) yield ("pack", urwid.Text(("link", m["url"])))
poll = status.data.get("poll") if status.poll:
if poll:
yield ("pack", urwid.Divider()) yield ("pack", urwid.Divider())
yield ("pack", self.build_linebox(self.poll_generator(poll))) yield ("pack", self.build_linebox(self.poll_generator(status.poll)))
card = status.data.get("card") if status.card:
if card:
yield ("pack", urwid.Divider()) yield ("pack", urwid.Divider())
yield ("pack", self.build_linebox(self.card_generator(card))) yield ("pack", self.build_linebox(self.card_generator(status.card)))
application = status.data.get("application") or {} application_name = status.application.name if status.application else None
application = application.get("name")
yield ("pack", urwid.AttrWrap(urwid.Divider("-"), "gray")) yield ("pack", urwid.AttrWrap(urwid.Divider("-"), "gray"))
translated_from = ( translated_from = (
language_name(status.translated_from) language_name(meta.translated_from)
if status.show_translation and status.translated_from if meta.show_translation and meta.translated_from
else None else None
) )
@ -379,7 +378,7 @@ class StatusDetails(urwid.Pile):
("yellow" if status.favourited else "gray", f"{status.data['favourites_count']}"), ("yellow" if status.favourited else "gray", f"{status.data['favourites_count']}"),
(visibility_color, f" · {visibility}"), (visibility_color, f" · {visibility}"),
("yellow", f" · Translated from {translated_from} " if translated_from else ""), ("yellow", f" · Translated from {translated_from} " if translated_from else ""),
("gray", f" · {application}" if application else ""), ("gray", f" · {application_name}" if application_name else ""),
])) ]))
# Push things to bottom # Push things to bottom
@ -390,45 +389,44 @@ class StatusDetails(urwid.Pile):
contents = urwid.Padding(contents, left=1, right=1) contents = urwid.Padding(contents, left=1, right=1)
return urwid.LineBox(contents) return urwid.LineBox(contents)
def card_generator(self, card): def card_generator(self, card: PreviewCard):
yield urwid.Text(("green", card["title"].strip())) yield urwid.Text(("green", card.title.strip()))
if card.get("author_name"): if card.author_name:
yield urwid.Text(["by ", ("yellow", card["author_name"].strip())]) yield urwid.Text(["by ", ("yellow", card.author_name.strip())])
yield urwid.Text("") yield urwid.Text("")
if card["description"]: if card.description:
yield urwid.Text(card["description"].strip()) yield urwid.Text(card.description.strip())
yield urwid.Text("") yield urwid.Text("")
yield urwid.Text(("link", card["url"])) yield urwid.Text(("link", card.url))
def poll_generator(self, poll): def poll_generator(self, poll: Poll):
for idx, option in enumerate(poll["options"]): for idx, option in enumerate(poll.options):
perc = (round(100 * option["votes_count"] / poll["votes_count"]) option_votes_count = option.votes_count or 0
if poll["votes_count"] else 0) perc = (round(100 * option_votes_count / poll.votes_count)
if poll.votes_count else 0)
if poll["voted"] and poll["own_votes"] and idx in poll["own_votes"]: if poll.voted and poll.own_votes and idx in poll.own_votes:
voted_for = "" voted_for = ""
else: else:
voted_for = "" voted_for = ""
yield urwid.Text(option["title"] + voted_for) yield urwid.Text(option.title + voted_for)
yield urwid.ProgressBar("", "poll_bar", perc) yield urwid.ProgressBar("", "poll_bar", perc)
status = "Poll · {} votes".format(poll["votes_count"]) status = "Poll · {} votes".format(poll.votes_count)
if poll["expired"]: if poll.expired:
status += " · Closed" status += " · Closed"
if poll["expires_at"]: if poll.expires_at:
expires_at = parse_datetime(poll["expires_at"]).strftime("%Y-%m-%d %H:%M") expires_at = poll.expires_at.strftime("%Y-%m-%d %H:%M")
status += " · Closes on {}".format(expires_at) status += " · Closes on {}".format(expires_at)
yield urwid.Text(("gray", status)) yield urwid.Text(("gray", status))
class StatusListItem(SelectableColumns): class StatusListItem(SelectableColumns):
def __init__(self, status): def __init__(self, status: Status):
edited_at = status.data.get("edited_at")
# TODO: hacky implementation to avoid creating conflicts for existing # TODO: hacky implementation to avoid creating conflicts for existing
# pull reuqests, refactor when merged. # pull reuqests, refactor when merged.
created_at = ( created_at = (
@ -437,11 +435,14 @@ class StatusListItem(SelectableColumns):
else status.created_at.strftime("%Y-%m-%d %H:%M") else status.created_at.strftime("%Y-%m-%d %H:%M")
) )
edited_flag = "*" if edited_at else " " edited_flag = "*" if status.edited_at else " "
favourited = ("yellow", "") if status.original.favourited else " " favourited = ("yellow", "") if status.original.favourited else " "
reblogged = ("yellow", "") if status.original.reblogged else " " reblogged = ("yellow", "") if status.original.reblogged else " "
is_reblog = ("cyan", "") if status.reblog else " " is_reblog = ("cyan", "") if status.reblog else " "
is_reply = ("cyan", "") if status.original.in_reply_to else " " is_reply = ("cyan", "") if status.original.in_reply_to_id else " "
# TODO: Add server name for home accounts?
account = status.original.account.acct
return super().__init__([ return super().__init__([
("pack", SelectableText(("blue", created_at), wrap="clip")), ("pack", SelectableText(("blue", created_at), wrap="clip")),
@ -451,7 +452,7 @@ class StatusListItem(SelectableColumns):
("pack", urwid.Text(" ")), ("pack", urwid.Text(" ")),
("pack", urwid.Text(reblogged)), ("pack", urwid.Text(reblogged)),
("pack", urwid.Text(" ")), ("pack", urwid.Text(" ")),
urwid.Text(("green", status.original.account), wrap="clip"), urwid.Text(("green", account), wrap="clip"),
("pack", urwid.Text(is_reply)), ("pack", urwid.Text(is_reply)),
("pack", urwid.Text(is_reblog)), ("pack", urwid.Text(is_reblog)),
("pack", urwid.Text(" ")), ("pack", urwid.Text(" ")),