Implement tree thread
This commit is contained in:
parent
55b02ccf4e
commit
6952577957
4 changed files with 98 additions and 22 deletions
|
@ -32,3 +32,20 @@ def get_config_dir():
|
|||
|
||||
# Default to ~/.config/witchie/
|
||||
return join(expanduser("~"), ".config", CONFIG_DIR_NAME)
|
||||
|
||||
|
||||
def get_data_dir():
|
||||
"""Returns the path to witchie data directory"""
|
||||
|
||||
# On Windows, store the config in roaming appdata
|
||||
if sys.platform == "win32" and "APPDATA" in os.environ:
|
||||
return join(os.getenv("APPDATA"), CONFIG_DIR_NAME)
|
||||
|
||||
# Respect XDG_CONFIG_HOME env variable if set
|
||||
# https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
|
||||
if "XDG_DATA_HOME" in os.environ:
|
||||
config_home = expanduser(os.environ["XDG_DATA_HOME"])
|
||||
return join(config_home, CONFIG_DIR_NAME)
|
||||
|
||||
# Default to ~/.config/witchie/
|
||||
return join(expanduser("~"), ".local/share", CONFIG_DIR_NAME)
|
||||
|
|
|
@ -14,7 +14,7 @@ from witchie.output import (print_account, print_acct_list, print_instance,
|
|||
print_list_accounts, print_lists,
|
||||
print_notifications, print_out,
|
||||
print_search_results, print_status, print_table,
|
||||
print_tag_list, print_timeline, print_user_list)
|
||||
print_tag_list, print_timeline, print_tree, print_user_list)
|
||||
from witchie.utils import (EOF_KEY, args_get_instance, delete_tmp_status_file,
|
||||
editor_input, multiline_input)
|
||||
from witchie.utils.datetime import parse_datetime
|
||||
|
@ -85,6 +85,17 @@ def status(app, user, args):
|
|||
print_status(status)
|
||||
|
||||
|
||||
def _get_replies_tree(status_id: int, descendants: list):
|
||||
tree = []
|
||||
for d in descendants:
|
||||
if d['in_reply_to_id'] == status_id:
|
||||
tree.append({
|
||||
'status': from_dict(Status, d),
|
||||
'replies': _get_replies_tree(d['id'], descendants)
|
||||
})
|
||||
return tree
|
||||
|
||||
|
||||
def thread(app, user, args):
|
||||
context_response = api.context(app, user, args.status_id)
|
||||
|
||||
|
@ -94,8 +105,25 @@ def thread(app, user, args):
|
|||
post = api.fetch_status(app, user, args.status_id).json()
|
||||
context = context_response.json()
|
||||
|
||||
statuses = chain(context["ancestors"], [post], context["descendants"])
|
||||
print_timeline(from_dict(Status, s) for s in statuses)
|
||||
if args.tree:
|
||||
reply_to = post["in_reply_to_id"]
|
||||
ancestors_map = {status['id']: status for status in context["ancestors"]}
|
||||
ancestors = []
|
||||
while reply_to:
|
||||
ancestors.append(ancestors_map[reply_to])
|
||||
reply_to = ancestors_map[reply_to]['in_reply_to_id']
|
||||
ancestors = reversed(ancestors)
|
||||
|
||||
print_timeline([from_dict(Status, s) for s in ancestors], padding=1)
|
||||
|
||||
descendants = {
|
||||
'status': from_dict(Status, post),
|
||||
'replies': _get_replies_tree(args.status_id, context["descendants"])
|
||||
}
|
||||
print_tree(descendants)
|
||||
else:
|
||||
statuses = chain(context["ancestors"], [post], context["descendants"])
|
||||
print_timeline(from_dict(Status, s) for s in statuses)
|
||||
|
||||
|
||||
def post(app, user, args):
|
||||
|
|
|
@ -473,6 +473,11 @@ READ_COMMANDS = [
|
|||
(["status_id"], {
|
||||
"help": "Show thread for post.",
|
||||
}),
|
||||
(["-t", "--tree"], {
|
||||
"action": 'store_true',
|
||||
"default": False,
|
||||
"help": "format the thread as tree (only makes sense for akkoma/pleroma)",
|
||||
}),
|
||||
json_arg,
|
||||
],
|
||||
require_auth=True,
|
||||
|
@ -546,7 +551,8 @@ POST_COMMANDS = [
|
|||
(["-A", "--include-mentions"], {
|
||||
"action": 'store_true',
|
||||
"default": False,
|
||||
"help": "include mentions of accounts mentioned in the post you're replying to and the account of the author themself",
|
||||
"help": "include mentions of accounts mentioned in the post you're replying to"
|
||||
" and the account of the author themself (may make it slower)",
|
||||
}),
|
||||
(["-l", "--language"], {
|
||||
"type": language,
|
||||
|
|
|
@ -277,7 +277,7 @@ def print_search_results(results):
|
|||
print_out("<yellow>Nothing found</yellow>")
|
||||
|
||||
|
||||
def print_status(status: Status, width: int = 80):
|
||||
def print_status(status: Status, width: int = 80, padding: int = 0):
|
||||
status_id = status.original.id
|
||||
in_reply_to_id = status.in_reply_to_id
|
||||
reblogged_by = status.account if status.reblog else None
|
||||
|
@ -296,51 +296,55 @@ def print_status(status: Status, width: int = 80):
|
|||
for react in status.emoji_reactions])
|
||||
|
||||
print_out(
|
||||
"│" * padding,
|
||||
f"<green>{display_name}</green>" if display_name else "",
|
||||
f"<blue>{username}</blue>",
|
||||
" " * spacing,
|
||||
f"<yellow>{time}</yellow>",
|
||||
)
|
||||
|
||||
print_out("")
|
||||
print_out("│" * padding)
|
||||
if status.spoiler_text:
|
||||
print_out("<yellow>Subject</yellow>: ", end="")
|
||||
print_out("│" * padding, "<yellow>Subject</yellow>: ", end="")
|
||||
print_html(status.spoiler_text)
|
||||
print()
|
||||
print_html(status.content, width)
|
||||
print_html(status.content, width, padding)
|
||||
|
||||
if status.media_attachments:
|
||||
print_out("\nMedia:")
|
||||
print_out(f"\n{"│" * padding}Media:")
|
||||
for count, attachment in enumerate(status.media_attachments):
|
||||
url = attachment.url
|
||||
description = f'Description: {attachment.description}'
|
||||
description = f'{"│" * padding}Description: {attachment.description}'
|
||||
print_out(f'{count+1}. <yellow>URL</yellow>: {url}')
|
||||
for i, line in enumerate(wc_wrap(description, width)):
|
||||
if i == 0:
|
||||
line = line.replace('Description', '<yellow>Description</yellow>')
|
||||
print_out(line)
|
||||
print_out("│" * padding + line)
|
||||
|
||||
if status.poll:
|
||||
print_poll(status.poll)
|
||||
|
||||
print_out()
|
||||
print_out("│" * padding)
|
||||
if reacts:
|
||||
reacts = "│" * padding + " " + reacts
|
||||
|
||||
print_out(
|
||||
f"<yellow>Reacts:</yellow>\n{reacts}\n" if reacts else "",
|
||||
"│" * padding,
|
||||
f"<yellow>Reacts:</yellow>\n{reacts}\n{'│' * padding}" if reacts else "",
|
||||
f"ID <yellow>{status_id}</yellow> ",
|
||||
f"↲ In reply to <yellow>{in_reply_to_id}</yellow> " if in_reply_to_id else "",
|
||||
f"↻ <blue>@{reblogged_by.acct}</blue> boosted " if reblogged_by else "",
|
||||
)
|
||||
|
||||
|
||||
def print_html(text, width=80):
|
||||
def print_html(text, width=80, padding=0):
|
||||
first = True
|
||||
for paragraph in html_to_paragraphs(text):
|
||||
if not first:
|
||||
print_out("")
|
||||
print_out("│" * padding)
|
||||
for line in paragraph:
|
||||
for subline in wc_wrap(line, width):
|
||||
print_out(highlight_hashtags(subline))
|
||||
print_out("│" * padding, highlight_hashtags(subline))
|
||||
first = False
|
||||
|
||||
|
||||
|
@ -370,11 +374,32 @@ def print_poll(poll: Poll):
|
|||
print_out(poll_footer)
|
||||
|
||||
|
||||
def print_timeline(items: Iterable[Status], width=100):
|
||||
print_out("─" * width)
|
||||
def print_timeline(items: Iterable[Status], width=80, padding=0):
|
||||
width -= padding
|
||||
if padding:
|
||||
first_paddings = "│" * (padding - 1) + "┌"
|
||||
paddings = "│" * (padding - 1) + "├"
|
||||
else:
|
||||
first_paddings = ""
|
||||
paddings = ""
|
||||
print_out(first_paddings + "─" * width)
|
||||
for item in items:
|
||||
print_status(item, width)
|
||||
print_out("─" * width)
|
||||
print_status(item, width - padding, padding)
|
||||
print_out(paddings + "─" * width)
|
||||
|
||||
|
||||
def print_tree(tree, depth=0):
|
||||
"""Print a thread tree"""
|
||||
if depth >= 20:
|
||||
print_out(" " * 20 + "(Thread goes too deep)")
|
||||
if depth:
|
||||
paddings = "│" * (depth - 1) + "└"
|
||||
else:
|
||||
paddings = ""
|
||||
print_status(tree['status'], 80-depth, padding=depth)
|
||||
print_out(paddings + "─" * 80)
|
||||
for reply in tree['replies']:
|
||||
print_tree(reply, depth+1)
|
||||
|
||||
|
||||
notification_msgs = {
|
||||
|
@ -385,7 +410,7 @@ notification_msgs = {
|
|||
}
|
||||
|
||||
|
||||
def print_notification(notification: Notification, width=100):
|
||||
def print_notification(notification: Notification, width=80):
|
||||
account = f"{notification.account.display_name} @{notification.account.acct}"
|
||||
msg = notification_msgs.get(notification.type)
|
||||
if msg is None:
|
||||
|
@ -397,7 +422,7 @@ def print_notification(notification: Notification, width=100):
|
|||
print_status(notification.status, width)
|
||||
|
||||
|
||||
def print_notifications(notifications: List[Notification], width=100):
|
||||
def print_notifications(notifications: List[Notification], width=80):
|
||||
for notification in notifications:
|
||||
print_notification(notification)
|
||||
print_out("─" * width)
|
||||
|
|
Loading…
Reference in a new issue