kopia lustrzana https://github.com/snarfed/bridgy-fed
UI: refactor actor link rendering into User.user_link / Object.actor_link
rodzic
ea5a4cf9c3
commit
b6be345921
48
models.py
48
models.py
|
@ -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'· {self.handle}' if handle else ''
|
||||
|
||||
return f"""\
|
||||
<a class="h-card u-author" href="{self.web_url()}">
|
||||
<span class="logo" title="{self.__class__.__name__}">{self.LOGO_HTML}</span>
|
||||
<a class="h-card u-author" href="{url}" title="{self.name()}">
|
||||
{img}
|
||||
{self.name()}
|
||||
</a>"""
|
||||
{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)}
|
||||
|
|
|
@ -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 class="follower col-xs-10 col-sm-10 col-lg-6">
|
||||
{{ f.user.user_link(handle=True)|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 %}
|
||||
|
||||
{% if page_name == 'following' %}
|
||||
<form method="post" action="/unfollow/start" class="col-xs-2 col-sm-1 col-lg-1">
|
||||
|
|
|
@ -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 %}
|
||||
...
|
||||
|
|
|
@ -30,23 +30,8 @@
|
|||
|
||||
<div class="row">
|
||||
<span class="big">
|
||||
{% if user.name() != user.handle_or_id() %}
|
||||
{{ user.user_link()|safe }}
|
||||
·
|
||||
{% 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>
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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={
|
||||
|
|
|
@ -30,8 +30,8 @@ ACTOR_WITH_PREFERRED_USERNAME = {
|
|||
|
||||
|
||||
def contents(activities):
|
||||
return [util.parse_html((a.get('object') or a)['content'].splitlines()[0]
|
||||
).get_text().strip()
|
||||
return [
|
||||
' '.join(util.parse_html((a.get('object') or a)['content']).get_text().split())
|
||||
for a in activities]
|
||||
|
||||
|
||||
|
@ -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):
|
||||
|
|
|
@ -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 = {}
|
||||
|
|
Ładowanie…
Reference in New Issue