Files
indiekit-endpoint-activitypub/views/activitypub-my-profile.njk
Ricardo df5ccc397e fix: ensure attachment is always an array for Mastodon compatibility
Fedify's JSON-LD compaction collapses single-element arrays to plain
objects. Mastodon checks `attachment.is_a?(Array)` and silently skips
non-array values, causing profile links to never display.

Also adds profile links section to the my-profile admin page and
fixes rel=me on the public profile page for bidirectional verification.
2026-02-24 11:39:42 +01:00

103 lines
4.1 KiB
Plaintext

{% extends "layouts/ap-reader.njk" %}
{% from "prose/macro.njk" import prose with context %}
{% block readercontent %}
{# Profile header #}
<div class="ap-my-profile">
{% if profile.image %}
<div class="ap-my-profile__header">
<img src="{{ profile.image }}" alt="" class="ap-my-profile__header-img" loading="lazy" crossorigin="anonymous">
</div>
{% endif %}
<div class="ap-my-profile__info">
<div class="ap-my-profile__avatar-wrap">
{% if profile.icon %}
<img src="{{ profile.icon }}" alt="{{ profile.name }}" class="ap-my-profile__avatar" loading="lazy" crossorigin="anonymous">
{% else %}
<span class="ap-my-profile__avatar ap-my-profile__avatar--placeholder">{{ profile.name[0] | upper if profile.name else "?" }}</span>
{% endif %}
</div>
<div class="ap-my-profile__meta">
<h2 class="ap-my-profile__name">{{ profile.name or handle }}</h2>
<div class="ap-my-profile__handle">{{ fullHandle }}</div>
{% if profile.summary %}
<div class="ap-my-profile__bio">{{ profile.summary | safe }}</div>
{% endif %}
{% if profile.attachments and profile.attachments.length > 0 %}
<dl class="ap-my-profile__fields">
{% for field in profile.attachments %}
<div class="ap-my-profile__field">
<dt class="ap-my-profile__field-name">{{ field.name }}</dt>
<dd class="ap-my-profile__field-value">
{% if field.value and (field.value.startsWith("http://") or field.value.startsWith("https://")) %}
<a href="{{ field.value }}" rel="me noopener" target="_blank">{{ field.value | replace("https://", "") | replace("http://", "") }}</a>
{% else %}
{{ field.value }}
{% endif %}
</dd>
</div>
{% endfor %}
</dl>
{% endif %}
</div>
<div class="ap-my-profile__stats">
<a href="{{ mountPath }}/admin/followers" class="ap-my-profile__stat">
<strong>{{ followerCount }}</strong> {{ __("activitypub.followers") }}
</a>
<a href="{{ mountPath }}/admin/following" class="ap-my-profile__stat">
<strong>{{ followingCount }}</strong> {{ __("activitypub.following") }}
</a>
<span class="ap-my-profile__stat">
<strong>{{ postCount }}</strong> {{ __("activitypub.myProfile.posts") }}
</span>
</div>
<a href="{{ mountPath }}/admin/profile" class="ap-my-profile__edit">
{{ __("activitypub.myProfile.editProfile") }}
</a>
</div>
</div>
{# Tab navigation #}
{% set profileBase = mountPath + "/admin/my-profile" %}
<nav class="ap-tabs" role="tablist">
<a href="{{ profileBase }}?tab=posts" class="ap-tab{% if tab == 'posts' %} ap-tab--active{% endif %}" role="tab">
{{ __("activitypub.myProfile.tabs.posts") }}
</a>
<a href="{{ profileBase }}?tab=replies" class="ap-tab{% if tab == 'replies' %} ap-tab--active{% endif %}" role="tab">
{{ __("activitypub.myProfile.tabs.replies") }}
</a>
<a href="{{ profileBase }}?tab=likes" class="ap-tab{% if tab == 'likes' %} ap-tab--active{% endif %}" role="tab">
{{ __("activitypub.myProfile.tabs.likes") }}
</a>
<a href="{{ profileBase }}?tab=boosts" class="ap-tab{% if tab == 'boosts' %} ap-tab--active{% endif %}" role="tab">
{{ __("activitypub.myProfile.tabs.boosts") }}
</a>
</nav>
{# Activity items #}
{% if items.length > 0 %}
<div class="ap-timeline" data-mount-path="{{ mountPath }}">
{% for item in items %}
{% include "partials/ap-item-card.njk" %}
{% endfor %}
</div>
{# Pagination — preserve active tab #}
{% if before %}
<nav class="ap-pagination">
<a href="?tab={{ tab }}&before={{ before }}" class="ap-pagination__next">
{{ __("activitypub.reader.pagination.older") }}
</a>
</nav>
{% endif %}
{% else %}
{{ prose({ text: __("activitypub.myProfile.empty") }) }}
{% endif %}
{% endblock %}