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
from dataclasses import dataclass, is_dataclass
@ -75,6 +79,9 @@ class Application:
@dataclass
class MediaAttachment:
"""
https://docs.joinmastodon.org/entities/MediaAttachment/
"""
id: str
type: str
url: str
@ -133,6 +140,9 @@ class Poll:
@dataclass
class PreviewCard:
"""
https://docs.joinmastodon.org/entities/PreviewCard/
"""
url: str
title: str
description: str
@ -180,6 +190,17 @@ class FilterResult:
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
class Status:
id: str
@ -214,27 +235,13 @@ class Status:
pinned: Optional[bool]
filtered: Optional[List[FilterResult]]
_meta: StatusMeta = dataclasses.field(default_factory=StatusMeta)
@property
def original(self) -> "Status":
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
T = TypeVar("T")

Wyświetl plik

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

Wyświetl plik

@ -5,7 +5,7 @@ import webbrowser
from typing import Optional
from .entities import Status
from .entities import Account, Poll, PreviewCard, Status
from .scroll import Scrollable, ScrollBar
from .utils import highlight_hashtags, parse_datetime, highlight_keys
from .widgets import SelectableText, SelectableColumns
@ -302,36 +302,38 @@ class StatusDetails(urwid.Pile):
self.status = status
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)
if status else ())
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:
text = "{} boosted".format(reblogged_by.display_name or reblogged_by.username)
yield ("pack", urwid.Text(("gray", text)))
yield ("pack", urwid.AttrMap(urwid.Divider("-"), "gray"))
if status.author.display_name:
yield ("pack", urwid.Text(("green", status.author.display_name)))
if status.account.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())
if status.data["spoiler_text"]:
yield ("pack", urwid.Text(status.data["spoiler_text"]))
if status.spoiler_text:
yield ("pack", urwid.Text(status.spoiler_text))
yield ("pack", urwid.Divider())
# 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.")))
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):
yield ("pack", urwid.Text(highlight_hashtags(line, self.followed_tags)))
media = status.data["media_attachments"]
media = status.media_attachments
if media:
for m in media:
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(("link", m["url"])))
poll = status.data.get("poll")
if poll:
if status.poll:
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 card:
if status.card:
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 = application.get("name")
application_name = status.application.name if status.application else None
yield ("pack", urwid.AttrWrap(urwid.Divider("-"), "gray"))
translated_from = (
language_name(status.translated_from)
if status.show_translation and status.translated_from
language_name(meta.translated_from)
if meta.show_translation and meta.translated_from
else None
)
@ -379,7 +378,7 @@ class StatusDetails(urwid.Pile):
("yellow" if status.favourited else "gray", f"{status.data['favourites_count']}"),
(visibility_color, f" · {visibility}"),
("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
@ -390,45 +389,44 @@ class StatusDetails(urwid.Pile):
contents = urwid.Padding(contents, left=1, right=1)
return urwid.LineBox(contents)
def card_generator(self, card):
yield urwid.Text(("green", card["title"].strip()))
if card.get("author_name"):
yield urwid.Text(["by ", ("yellow", card["author_name"].strip())])
def card_generator(self, card: PreviewCard):
yield urwid.Text(("green", card.title.strip()))
if card.author_name:
yield urwid.Text(["by ", ("yellow", card.author_name.strip())])
yield urwid.Text("")
if card["description"]:
yield urwid.Text(card["description"].strip())
if card.description:
yield urwid.Text(card.description.strip())
yield urwid.Text("")
yield urwid.Text(("link", card["url"]))
yield urwid.Text(("link", card.url))
def poll_generator(self, poll):
for idx, option in enumerate(poll["options"]):
perc = (round(100 * option["votes_count"] / poll["votes_count"])
if poll["votes_count"] else 0)
def poll_generator(self, poll: Poll):
for idx, option in enumerate(poll.options):
option_votes_count = option.votes_count or 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 = ""
else:
voted_for = ""
yield urwid.Text(option["title"] + voted_for)
yield urwid.Text(option.title + voted_for)
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"
if poll["expires_at"]:
expires_at = parse_datetime(poll["expires_at"]).strftime("%Y-%m-%d %H:%M")
if poll.expires_at:
expires_at = poll.expires_at.strftime("%Y-%m-%d %H:%M")
status += " · Closes on {}".format(expires_at)
yield urwid.Text(("gray", status))
class StatusListItem(SelectableColumns):
def __init__(self, status):
edited_at = status.data.get("edited_at")
def __init__(self, status: Status):
# TODO: hacky implementation to avoid creating conflicts for existing
# pull reuqests, refactor when merged.
created_at = (
@ -437,11 +435,14 @@ class StatusListItem(SelectableColumns):
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 " "
reblogged = ("yellow", "") if status.original.reblogged 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__([
("pack", SelectableText(("blue", created_at), wrap="clip")),
@ -451,7 +452,7 @@ class StatusListItem(SelectableColumns):
("pack", urwid.Text(" ")),
("pack", urwid.Text(reblogged)),
("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_reblog)),
("pack", urwid.Text(" ")),