From 5dd53b1b9cec240e4867a1087a4f379b2632dce2 Mon Sep 17 00:00:00 2001 From: Lexi Winter Date: Sun, 31 Dec 2023 17:13:42 +0000 Subject: [PATCH] tui: honour user's default visibility preference Mastodon allows the user to configure a default visibility which should apply to all clients. This setting is returned by the /api/v1/preferences method. Fetch the user preferences when the TUI starts, and use it to set the default visibility when composing a new toot. The preference can be overridden by a new command-line option, toot tui --default-visibility=. If neither the preference nor the option are set, fall back to get_default_visibility(). --- toot/api.py | 4 ++++ toot/cli/tui.py | 9 ++++++++- toot/tui/app.py | 26 +++++++++++++++++++++++++- toot/tui/compose.py | 6 ++---- 4 files changed, 39 insertions(+), 6 deletions(-) diff --git a/toot/api.py b/toot/api.py index 5071df4..0033eb4 100644 --- a/toot/api.py +++ b/toot/api.py @@ -618,6 +618,10 @@ def get_instance(base_url: str) -> Response: return http.anon_get(url) +def get_preferences(app, user) -> Response: + return http.get(app, user, '/api/v1/preferences') + + def get_lists(app, user): return http.get(app, user, "/api/v1/lists").json() diff --git a/toot/cli/tui.py b/toot/cli/tui.py index 1cedd75..391f8e3 100644 --- a/toot/cli/tui.py +++ b/toot/cli/tui.py @@ -1,7 +1,7 @@ import click from typing import Optional -from toot.cli import TUI_COLORS, Context, cli, pass_context +from toot.cli import TUI_COLORS, VISIBILITY_CHOICES, Context, cli, pass_context from toot.cli.validators import validate_tui_colors from toot.tui.app import TUI, TuiOptions @@ -24,12 +24,18 @@ COLOR_OPTIONS = ", ".join(TUI_COLORS.keys()) help=f"""Number of colors to use, one of {COLOR_OPTIONS}, defaults to 16 if using --color, and 1 if using --no-color.""" ) +@click.option( + "-v", "--default-visibility", + type=click.Choice(VISIBILITY_CHOICES), + help="Default visibility when posting new toots; overrides the server-side preference" +) @pass_context def tui( ctx: Context, colors: Optional[int], media_viewer: Optional[str], relative_datetimes: bool, + default_visibility: Optional[str] ): """Launches the toot terminal user interface""" if colors is None: @@ -39,6 +45,7 @@ def tui( colors=colors, media_viewer=media_viewer, relative_datetimes=relative_datetimes, + default_visibility=default_visibility ) tui = TUI.create(ctx.app, ctx.user, options) tui.run() diff --git a/toot/tui/app.py b/toot/tui/app.py index f67dcf5..5d9982b 100644 --- a/toot/tui/app.py +++ b/toot/tui/app.py @@ -31,6 +31,7 @@ class TuiOptions(NamedTuple): colors: int media_viewer: Optional[str] relative_datetimes: bool + default_visibility: Optional[bool] class Header(urwid.WidgetWrap): @@ -137,11 +138,13 @@ class TUI(urwid.Frame): self.can_translate = False self.account = None self.followed_accounts = [] + self.preferences = {} super().__init__(self.body, header=self.header, footer=self.footer) def run(self): self.loop.set_alarm_in(0, lambda *args: self.async_load_instance()) + self.loop.set_alarm_in(0, lambda *args: self.async_load_preferences()) self.loop.set_alarm_in(0, lambda *args: self.async_load_timeline( is_initial=True, timeline_name="home")) self.loop.set_alarm_in(0, lambda *args: self.async_load_followed_accounts()) @@ -326,6 +329,19 @@ class TUI(urwid.Frame): return self.run_in_thread(_load_instance, done_callback=_done) + def async_load_preferences(self): + """ + Attempt to update user preferences from instance. + https://docs.joinmastodon.org/methods/preferences/ + """ + def _load_preferences(): + return api.get_preferences(self.app, self.user).json() + + def _done(preferences): + self.preferences = preferences + + return self.run_in_thread(_load_preferences, done_callback=_done) + def async_load_followed_accounts(self): def _load_accounts(): try: @@ -400,7 +416,15 @@ class TUI(urwid.Frame): def _post(timeline, *args): self.post_status(*args) - composer = StatusComposer(self.max_toot_chars, self.user.username, in_reply_to) + # If the user specified --default-visibility, use that; otherwise, + # try to use the server-side default visibility. If that fails, fall + # back to get_default_visibility(). + visibility = (self.options.default_visibility or + self.preferences.get('posting:default:visibility', + get_default_visibility())) + + composer = StatusComposer(self.max_toot_chars, self.user.username, + visibility, in_reply_to) urwid.connect_signal(composer, "close", _close) urwid.connect_signal(composer, "post", _post) self.open_overlay(composer, title="Compose status") diff --git a/toot/tui/compose.py b/toot/tui/compose.py index c4a038a..ea60fa6 100644 --- a/toot/tui/compose.py +++ b/toot/tui/compose.py @@ -1,8 +1,6 @@ import urwid import logging -from toot.cli import get_default_visibility - from .constants import VISIBILITY_OPTIONS from .widgets import Button, EditBox @@ -15,7 +13,7 @@ class StatusComposer(urwid.Frame): """ signals = ["close", "post"] - def __init__(self, max_chars, username, in_reply_to=None): + def __init__(self, max_chars, username, visibility, in_reply_to=None): self.in_reply_to = in_reply_to self.max_chars = max_chars self.username = username @@ -34,7 +32,7 @@ class StatusComposer(urwid.Frame): on_press=self.remove_content_warning) self.visibility = ( - in_reply_to.visibility if in_reply_to else get_default_visibility() + in_reply_to.visibility if in_reply_to else visibility ) self.visibility_button = Button("Visibility: {}".format(self.visibility), on_press=self.choose_visibility)