Files
indiekit-endpoint-activitypub/views/activitypub-remote-profile.njk
Ricardo 0cf49e037c fix: remove duplicate page headings across all AP templates
document.njk already renders title as h1 via the heading macro.
All 14 AP templates were also calling heading() with level 1 inside
their content block, producing two h1 elements per page. Removed
the redundant calls and moved dynamic count prefixes into the title
variable in followers/following controllers.
2026-02-21 21:32:56 +01:00

113 lines
4.1 KiB
Plaintext

{% extends "layouts/ap-reader.njk" %}
{% from "heading/macro.njk" import heading with context %}
{% from "prose/macro.njk" import prose with context %}
{% block readercontent %}
<div class="ap-profile"
x-data="{
following: {{ 'true' if isFollowing else 'false' }},
muted: {{ 'true' if isMuted else 'false' }},
blocked: {{ 'true' if isBlocked else 'false' }},
loading: false,
async action(endpoint, body) {
if (this.loading) return;
this.loading = true;
try {
const res = await fetch('{{ mountPath }}/admin/reader/' + endpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': '{{ csrfToken }}'
},
body: JSON.stringify(body)
});
const data = await res.json();
return data.success;
} catch { return false; }
finally { this.loading = false; }
}
}">
{# Header image #}
{% if image %}
<div class="ap-profile__header">
<img src="{{ image }}" alt="" class="ap-profile__header-img">
</div>
{% endif %}
{# Profile info #}
<div class="ap-profile__info">
<div class="ap-profile__avatar-wrap">
{% if icon %}
<img src="{{ icon }}" alt="{{ name }}" class="ap-profile__avatar"
onerror="this.replaceWith(Object.assign(document.createElement('div'),{className:'ap-profile__avatar ap-profile__avatar--placeholder',textContent:'{{ name[0] }}'}))">
{% else %}
<div class="ap-profile__avatar ap-profile__avatar--placeholder">{{ name[0] }}</div>
{% endif %}
</div>
<div class="ap-profile__details">
<h2 class="ap-profile__name">{{ name }}</h2>
{% if actorHandle %}
<div class="ap-profile__handle">@{{ actorHandle }}@{{ instanceHost }}</div>
{% endif %}
{% if bio %}
<div class="ap-profile__bio">{{ bio | safe }}</div>
{% endif %}
</div>
{# Action buttons #}
<div class="ap-profile__actions">
<button class="ap-profile__action ap-profile__action--follow"
:class="{ 'ap-profile__action--active': following }"
:disabled="loading"
@click="
const ok = await action(following ? 'unfollow' : 'follow', { url: '{{ actorUrl }}' });
if (ok) following = !following;
">
<span x-text="following ? '{{ __('activitypub.profile.remote.unfollow') }}' : '{{ __('activitypub.profile.remote.follow') }}'"></span>
</button>
<button class="ap-profile__action"
:disabled="loading"
@click="
const ok = await action(muted ? 'unmute' : 'mute', { url: '{{ actorUrl }}' });
if (ok) muted = !muted;
">
<span x-text="muted ? '{{ __('activitypub.moderation.unmute') }}' : '{{ __('activitypub.moderation.muteActor') }}'"></span>
</button>
<button class="ap-profile__action ap-profile__action--danger"
:disabled="loading"
@click="
const ok = await action(blocked ? 'unblock' : 'block', { url: '{{ actorUrl }}' });
if (ok) blocked = !blocked;
">
<span x-text="blocked ? '{{ __('activitypub.moderation.unblock') }}' : '{{ __('activitypub.moderation.blockActor') }}'"></span>
</button>
<a href="{{ actorUrl }}" class="ap-profile__action" target="_blank" rel="noopener">
{{ __("activitypub.profile.remote.viewOn") }} {{ instanceHost }}
</a>
</div>
</div>
{# Posts from this actor #}
<div class="ap-profile__posts">
<h3>{{ __("activitypub.profile.remote.postsTitle") }}</h3>
{% if posts.length > 0 %}
<div class="ap-timeline">
{% for item in posts %}
{% include "partials/ap-item-card.njk" %}
{% endfor %}
</div>
{% elif isFollowing %}
{{ prose({ text: __("activitypub.profile.remote.noPosts") }) }}
{% else %}
{{ prose({ text: __("activitypub.profile.remote.followToSee") }) }}
{% endif %}
</div>
</div>
{% endblock %}