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().
This commit is contained in:
Lexi Winter 2023-12-31 17:13:42 +00:00
parent 4e55fba15e
commit 5dd53b1b9c
4 changed files with 39 additions and 6 deletions

View File

@ -618,6 +618,10 @@ def get_instance(base_url: str) -> Response:
return http.anon_get(url) return http.anon_get(url)
def get_preferences(app, user) -> Response:
return http.get(app, user, '/api/v1/preferences')
def get_lists(app, user): def get_lists(app, user):
return http.get(app, user, "/api/v1/lists").json() return http.get(app, user, "/api/v1/lists").json()

View File

@ -1,7 +1,7 @@
import click import click
from typing import Optional 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.cli.validators import validate_tui_colors
from toot.tui.app import TUI, TuiOptions 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 help=f"""Number of colors to use, one of {COLOR_OPTIONS}, defaults to 16 if
using --color, and 1 if using --no-color.""" 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 @pass_context
def tui( def tui(
ctx: Context, ctx: Context,
colors: Optional[int], colors: Optional[int],
media_viewer: Optional[str], media_viewer: Optional[str],
relative_datetimes: bool, relative_datetimes: bool,
default_visibility: Optional[str]
): ):
"""Launches the toot terminal user interface""" """Launches the toot terminal user interface"""
if colors is None: if colors is None:
@ -39,6 +45,7 @@ def tui(
colors=colors, colors=colors,
media_viewer=media_viewer, media_viewer=media_viewer,
relative_datetimes=relative_datetimes, relative_datetimes=relative_datetimes,
default_visibility=default_visibility
) )
tui = TUI.create(ctx.app, ctx.user, options) tui = TUI.create(ctx.app, ctx.user, options)
tui.run() tui.run()

View File

@ -31,6 +31,7 @@ class TuiOptions(NamedTuple):
colors: int colors: int
media_viewer: Optional[str] media_viewer: Optional[str]
relative_datetimes: bool relative_datetimes: bool
default_visibility: Optional[bool]
class Header(urwid.WidgetWrap): class Header(urwid.WidgetWrap):
@ -137,11 +138,13 @@ class TUI(urwid.Frame):
self.can_translate = False self.can_translate = False
self.account = None self.account = None
self.followed_accounts = [] self.followed_accounts = []
self.preferences = {}
super().__init__(self.body, header=self.header, footer=self.footer) super().__init__(self.body, header=self.header, footer=self.footer)
def run(self): 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_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( self.loop.set_alarm_in(0, lambda *args: self.async_load_timeline(
is_initial=True, timeline_name="home")) is_initial=True, timeline_name="home"))
self.loop.set_alarm_in(0, lambda *args: self.async_load_followed_accounts()) 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) 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 async_load_followed_accounts(self):
def _load_accounts(): def _load_accounts():
try: try:
@ -400,7 +416,15 @@ class TUI(urwid.Frame):
def _post(timeline, *args): def _post(timeline, *args):
self.post_status(*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, "close", _close)
urwid.connect_signal(composer, "post", _post) urwid.connect_signal(composer, "post", _post)
self.open_overlay(composer, title="Compose status") self.open_overlay(composer, title="Compose status")

View File

@ -1,8 +1,6 @@
import urwid import urwid
import logging import logging
from toot.cli import get_default_visibility
from .constants import VISIBILITY_OPTIONS from .constants import VISIBILITY_OPTIONS
from .widgets import Button, EditBox from .widgets import Button, EditBox
@ -15,7 +13,7 @@ class StatusComposer(urwid.Frame):
""" """
signals = ["close", "post"] 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.in_reply_to = in_reply_to
self.max_chars = max_chars self.max_chars = max_chars
self.username = username self.username = username
@ -34,7 +32,7 @@ class StatusComposer(urwid.Frame):
on_press=self.remove_content_warning) on_press=self.remove_content_warning)
self.visibility = ( 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), self.visibility_button = Button("Visibility: {}".format(self.visibility),
on_press=self.choose_visibility) on_press=self.choose_visibility)