UI: refactor actor link rendering into User.user_link / Object.actor_link

pull/984/head
Ryan Barrett 2024-04-29 12:26:54 -07:00
rodzic ea5a4cf9c3
commit b6be345921
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 6BE31FDF4776E9D4
8 zmienionych plików z 64 dodań i 57 usunięć

Wyświetl plik

@ -592,15 +592,32 @@ class User(StringIdModel, metaclass=ProtocolUserMeta):
if copy.protocol in (proto.LABEL, proto.ABBREV):
return copy.uri
def user_link(self):
"""Returns a pretty link to the external user with name and profile picture."""
def user_link(self, handle=False, maybe_internal_link=True):
"""Returns a pretty link to the user with name and profile picture.
If they're opted in, links to their Bridgy Fed user page. Otherwise,
links to their external account.
TODO: unify with :meth:`Object.actor_link`?
Args:
handle (bool): include handle as well as display name
maybe_internal_link (bool): if True, link to Bridgy Fed user page
instead of external account
"""
url = (self.user_page_path()
if self.enabled_protocols or self.LABEL == 'web' or self.direct
else self.web_url())
pic = self.profile_picture()
img = f'<img src="{pic}" class="profile"> ' if pic else ''
img = f'<img src="{pic}" class="profile">' if pic else ''
maybe_handle = f'&middot; {self.handle}' if handle else ''
return f"""\
<a class="h-card u-author" href="{self.web_url()}">
{img}
{self.name()}
</a>"""
<span class="logo" title="{self.__class__.__name__}">{self.LOGO_HTML}</span>
<a class="h-card u-author" href="{url}" title="{self.name()}">
{img}
{util.ellipsize(self.name(), chars=40)} {maybe_handle}
</a>"""
def profile_picture(self):
"""Returns the user's profile picture image URL, if available, or None."""
@ -941,6 +958,8 @@ class Object(StringIdModel):
def actor_link(self, image=True, sized=False, user=None):
"""Returns a pretty HTML link with the actor's name and picture.
TODO: unify with :meth:`User.user_link`?
Args:
image (bool): whether to include an ``img`` tag with the actor's picture
sized (bool): whether to set an explicit (``width=32``) size on the
@ -952,10 +971,12 @@ class Object(StringIdModel):
"""
attrs = {'class': 'h-card u-author'}
if user.key in self.users or user.key.id() in self.domains:
if user and (user.key in self.users or user.key.id() in self.domains):
# outbound; show a nice link to the user
return user.user_link()
proto = PROTOCOLS.get(self.source_protocol)
actor = None
if self.as1:
actor = (as1.get_object(self.as1, 'actor')
@ -963,7 +984,6 @@ class Object(StringIdModel):
# hydrate from datastore if available
# TODO: optimize! this is called serially in loops, eg in home.html
if set(actor.keys()) == {'id'} and self.source_protocol:
proto = PROTOCOLS[self.source_protocol]
actor_obj = proto.load(actor['id'], remote=False)
if actor_obj and actor_obj.as1:
actor = actor_obj.as1
@ -979,7 +999,19 @@ class Object(StringIdModel):
if not image or not img_url:
return common.pretty_link(url, text=name, attrs=attrs, user=user)
# from protocol import Protocol
# if actor_proto := Protocol.for_id(actor['id']):
# if actor_user := actor_proto.get_by_id(actor['id']):
# if actor_user and (actor_user.direct or actor_user.enabled_protocols
# or actor_user.LABEL == 'web'):
# url = actor_user.user_page_path()
logo = ''
if proto:
logo = f'<span class="logo" title="{self.__class__.__name__}">{proto.LOGO_HTML}</span>'
return f"""\
{logo}
<a class="h-card u-author" href="{url}" title="{name}">
<img class="profile" src="{img_url}" {'width="32"' if sized else ''}/>
{util.ellipsize(name, chars=40)}

Wyświetl plik

@ -3,20 +3,9 @@
{% for f in followers %}
<li class="row">
{% with url=f.user.web_url(), user_as1=f.user.obj.as1 or {} %}
<a class="follower col-xs-10 col-sm-10 col-lg-6" href="{{ url }}">
<span class="logo" title="{{ user.__class__.__name__ }}">
{{ f.user.LOGO_HTML|safe }}
</span>
{% with picture=util.get_url(user_as1, 'icon') or util.get_url(user_as1, 'image') %}
{% if picture %}
<img class="profile u-photo" src="{{ picture }}" width="48px">
{% endif %}
{% endwith %}
{{ user_as1.get('displayName') or '' }}
{{ f.user.handle or url }}
</a>
{% endwith %}
<span class="follower col-xs-10 col-sm-10 col-lg-6">
{{ f.user.user_link(handle=True)|safe }}
</span>
{% if page_name == 'following' %}
<form method="post" action="/unfollow/start" class="col-xs-2 col-sm-1 col-lg-1">

Wyświetl plik

@ -4,9 +4,6 @@
<li class="row h-entry">
<div class="e-content col-xs-{{ 5 if show_users else 8 }}">
{% if show_activity_actors %}
{% if obj.source_protocol %}
<span class="logo">{{ PROTOCOLS[obj.source_protocol].LOGO_HTML|safe }}</span>
{% endif %}
{{ obj.actor_link(user=user)|safe }}
{% else %}
...

Wyświetl plik

@ -30,23 +30,8 @@
<div class="row">
<span class="big">
{% if user.name() != user.handle_or_id() %}
{{ user.user_link()|safe }}
&middot;
{% else %}
{% if user.profile_picture() %}
<img src="{{ user.profile_picture() }}" class="profile">
{% endif %}
{% endif %}
<nobr>
<a href="{{ user.web_url() }}"
title="{{ user.__class__.__name__ }} (original)">
<span class="logo">{{ user.LOGO_HTML|safe }}</span>
{{ user.handle_or_id() }}
</a>
{{ user.user_link(handle=True, maybe_internal_link=False)|safe }}
<form method="post" action="{{ user.user_page_path('update-profile') }}">
<button id="update-profile-button" type="submit" title="Update profile"
class="btn btn-default glyphicon glyphicon-refresh"></button>

Wyświetl plik

@ -30,10 +30,11 @@ class CommonTest(TestCase):
# current user's homepage gets converted to BF user page
self.assert_multiline_equals("""\
<a class="h-card u-author" href="https://user.com/">
<span class="logo" title="Web">🌐</span>
<a class="h-card u-author" href="/web/user.com" title="user.com">
user.com
</a>""", common.pretty_link('https://user.com/', user=Web(id='user.com')))
</a>""", common.pretty_link('https://user.com/', user=Web(id='user.com')),
ignore_blanks=True)
def test_redirect_wrap_empty(self):
self.assertIsNone(common.redirect_wrap(None))

Wyświetl plik

@ -167,15 +167,16 @@ class UserTest(TestCase):
def test_user_link(self):
self.assert_multiline_equals("""\
<a class="h-card u-author" href="https://y.z/">
<span class="logo" title="Web">🌐</span>
<a class="h-card u-author" href="/web/y.z" title="y.z">
y.z
</a>""", self.user.user_link())
</a>""", self.user.user_link(), ignore_blanks=True)
self.user.obj = Object(id='a', as2=ACTOR)
self.assert_multiline_equals("""\
<a class="h-card u-author" href="https://y.z/">
<img src="https://user.com/me.jpg" class="profile">
<span class="logo" title="Web">🌐</span>
<a class="h-card u-author" href="/web/y.z" title="Mrs. ☕ Foo">
<img src="https://user.com/me.jpg" class="profile">
Mrs. Foo
</a>""", self.user.user_link())
@ -520,7 +521,8 @@ class ObjectTest(TestCase):
):
with self.subTest(expected=expected, as2=as2):
obj = Object(id='x', as2=as2)
self.assert_multiline_in(expected, obj.actor_link())
self.assert_multiline_in(expected, obj.actor_link(),
ignore_blanks=True)
self.assertEqual(
'<a class="h-card u-author" href="http://foo">foo</a>',
@ -531,7 +533,7 @@ class ObjectTest(TestCase):
obj = Object(id='x', source_protocol='ui', users=[self.user.key])
got = obj.actor_link(user=self.user)
self.assertIn('href="fake:user">', got)
self.assertIn('href="fake:user" title="Alice">', got)
self.assertIn('Alice', got)
def test_actor_link_object_in_datastore(self):
@ -562,7 +564,7 @@ class ObjectTest(TestCase):
<a class="h-card u-author" href="" title="Alice">
<img class="profile" src="foo.jpg" width="32"/>
Alice
</a>""", obj.actor_link(sized=True))
</a>""", obj.actor_link(sized=True), ignore_blanks=True)
def test_actor_link_composite_url(self):
obj = Object(id='x', our_as1={

Wyświetl plik

@ -30,9 +30,9 @@ ACTOR_WITH_PREFERRED_USERNAME = {
def contents(activities):
return [util.parse_html((a.get('object') or a)['content'].splitlines()[0]
).get_text().strip()
for a in activities]
return [
' '.join(util.parse_html((a.get('object') or a)['content']).get_text().split())
for a in activities]
class PagesTest(TestCase):
@ -40,7 +40,7 @@ class PagesTest(TestCase):
EXPECTED_SNIPPETS = [
'Dr. Eve replied a comment',
'tag:fake.com:44... posted a mention',
'tag:fake.com:44... posted my note',
'🌐 user.com posted my note',
]
def setUp(self):

Wyświetl plik

@ -71,6 +71,7 @@ class Fake(User, protocol.Protocol):
PHRASE = 'fake-phrase'
CONTENT_TYPE = 'fa/ke'
HAS_COPIES = True
LOGO_HTML = '<img src="fake-logo">'
# maps string ids to dict AS1 objects that can be fetched
fetchable = {}