mirror of
https://github.com/ihabunek/toot.git
synced 2025-02-02 15:07:51 -05:00
Add an action to display status links and open them
We add a new [L]inks action that opens an overlay window with links found in the content of selected status. Links are selectable and upon click/enter we open the web browser at link's URL.
This commit is contained in:
parent
5fc46d0cfc
commit
28e1281187
@ -8,10 +8,10 @@ from toot import api, __version__
|
||||
from .compose import StatusComposer
|
||||
from .constants import PALETTE
|
||||
from .entities import Status
|
||||
from .overlays import ExceptionStackTrace, GotoMenu, Help, StatusSource
|
||||
from .overlays import ExceptionStackTrace, GotoMenu, Help, StatusSource, StatusLinks
|
||||
from .overlays import StatusDeleteConfirmation
|
||||
from .timeline import Timeline
|
||||
from .utils import show_media
|
||||
from .utils import parse_content_links, show_media
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@ -182,6 +182,9 @@ class TUI(urwid.Frame):
|
||||
def _source(timeline, status):
|
||||
self.show_status_source(status)
|
||||
|
||||
def _links(timeline, status):
|
||||
self.show_links(status)
|
||||
|
||||
def _media(timeline, status):
|
||||
self.show_media(status)
|
||||
|
||||
@ -197,6 +200,7 @@ class TUI(urwid.Frame):
|
||||
urwid.connect_signal(timeline, "reblog", self.async_toggle_reblog)
|
||||
urwid.connect_signal(timeline, "reply", _reply)
|
||||
urwid.connect_signal(timeline, "source", _source)
|
||||
urwid.connect_signal(timeline, "links", _links)
|
||||
|
||||
def build_timeline(self, name, statuses):
|
||||
def _close(*args):
|
||||
@ -302,6 +306,14 @@ class TUI(urwid.Frame):
|
||||
title="Status source",
|
||||
)
|
||||
|
||||
def show_links(self, status):
|
||||
links = parse_content_links(status.data["content"])
|
||||
self.open_overlay(
|
||||
widget=StatusLinks(links),
|
||||
title="Status links",
|
||||
options={"height": len(links) + 2},
|
||||
)
|
||||
|
||||
def show_exception(self, exception):
|
||||
self.open_overlay(
|
||||
widget=ExceptionStackTrace(exception),
|
||||
|
@ -20,6 +20,20 @@ class StatusSource(urwid.ListBox):
|
||||
super().__init__(walker)
|
||||
|
||||
|
||||
class StatusLinks(urwid.ListBox):
|
||||
"""Shows status links."""
|
||||
|
||||
def __init__(self, links):
|
||||
|
||||
def widget(url, title):
|
||||
return Button(title or url, on_press=lambda btn: webbrowser.open(url))
|
||||
|
||||
walker = urwid.SimpleFocusListWalker(
|
||||
[widget(url, title) for url, title in links]
|
||||
)
|
||||
super().__init__(walker)
|
||||
|
||||
|
||||
class ExceptionStackTrace(urwid.ListBox):
|
||||
"""Shows an exception stack trace."""
|
||||
def __init__(self, ex):
|
||||
@ -132,6 +146,7 @@ class Help(urwid.Padding):
|
||||
yield urwid.Text(h(" [R] - Reply to current status"))
|
||||
yield urwid.Text(h(" [S] - Show text marked as sensitive"))
|
||||
yield urwid.Text(h(" [T] - Show status thread (replies)"))
|
||||
yield urwid.Text(h(" [L] - Show the status links"))
|
||||
yield urwid.Text(h(" [U] - Show the status data in JSON as received from the server"))
|
||||
yield urwid.Text(h(" [V] - Open status in default browser"))
|
||||
yield urwid.Divider()
|
||||
|
@ -26,6 +26,7 @@ class Timeline(urwid.Columns):
|
||||
"reblog", # Reblog status
|
||||
"reply", # Compose a reply to a status
|
||||
"source", # Show status source
|
||||
"links", # Show status links
|
||||
"thread", # Show thread for status
|
||||
]
|
||||
|
||||
@ -140,6 +141,10 @@ class Timeline(urwid.Columns):
|
||||
self.refresh_status_details()
|
||||
return
|
||||
|
||||
if key in ("l", "L"):
|
||||
self._emit("links", status)
|
||||
return
|
||||
|
||||
if key in ("t", "T"):
|
||||
self._emit("thread", status)
|
||||
return
|
||||
@ -281,6 +286,7 @@ class StatusDetails(urwid.Pile):
|
||||
"[F]avourite",
|
||||
"[V]iew",
|
||||
"[T]hread" if not self.in_thread else "",
|
||||
"[L]inks",
|
||||
"[R]eply",
|
||||
"So[u]rce",
|
||||
"[H]elp",
|
||||
|
@ -1,3 +1,4 @@
|
||||
from html.parser import HTMLParser
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
@ -74,3 +75,30 @@ def show_media(paths):
|
||||
raise Exception("Cannot find an image viewer")
|
||||
|
||||
subprocess.run([viewer] + paths)
|
||||
|
||||
|
||||
class LinkParser(HTMLParser):
|
||||
|
||||
def reset(self):
|
||||
super().reset()
|
||||
self.links = []
|
||||
|
||||
def handle_starttag(self, tag, attrs):
|
||||
if tag == "a":
|
||||
href, title = None, None
|
||||
for name, value in attrs:
|
||||
if name == "href":
|
||||
href = value
|
||||
if name == "title":
|
||||
title = value
|
||||
if href:
|
||||
self.links.append((href, title))
|
||||
|
||||
|
||||
def parse_content_links(content):
|
||||
"""Parse <a> tags from status's `content` and return them as a list of
|
||||
(href, title), where `title` may be None.
|
||||
"""
|
||||
parser = LinkParser()
|
||||
parser.feed(content)
|
||||
return parser.links[:]
|
||||
|
Loading…
x
Reference in New Issue
Block a user