Files
indiekit-endpoint-activitypub/views/partials/ap-item-media.njk
Ricardo c243b70629 feat: enriched media model with ALT badges (Release 3+4)
Change photo storage from bare URL strings to objects with url, alt,
width, height (AP) plus blurhash and focus (Mastodon API). Templates
handle both old string and new object format for backward compat.

Add ALT text badges on gallery images — click to expand the full
alt text in an overlay. Renders in both reader and explore views.

Also pass alt text through to lightbox and quote embed photos.

Bump version to 2.5.3.

Confab-Link: http://localhost:8080/sessions/e9d666ac-3c90-4298-9e92-9ac9d142bc06
2026-03-03 13:46:58 +01:00

65 lines
3.3 KiB
Plaintext

{# Media attachments partial — included from ap-item-card.njk #}
{# Photo gallery with lightbox #}
{% if item.photo and item.photo.length > 0 %}
{% set displayCount = item.photo.length if item.photo.length < 4 else 4 %}
{% set extraCount = item.photo.length - 4 %}
{% set totalPhotos = item.photo.length %}
<div x-data="{ lightbox: false, idx: 0 }" class="ap-card__gallery ap-card__gallery--{{ displayCount }}">
{% for photo in item.photo %}
{# Support both old string format and new object format #}
{% set photoSrc = photo.url if photo.url else photo %}
{% set photoAlt = photo.alt if photo.alt else "" %}
{% if loop.index0 < 4 %}
<div class="ap-card__gallery-item" x-data="{ showAlt: false }">
<button type="button" @click="idx = {{ loop.index0 }}; lightbox = true" class="ap-card__gallery-link{% if loop.index0 == 3 and extraCount > 0 %} ap-card__gallery-link--more{% endif %}">
<img src="{{ photoSrc }}" alt="{{ photoAlt }}" loading="lazy">
{% if loop.index0 == 3 and extraCount > 0 %}
<span class="ap-card__gallery-more">+{{ extraCount }}</span>
{% endif %}
</button>
{% if photoAlt %}
<button type="button" class="ap-media__alt-badge" @click.stop="showAlt = !showAlt" :aria-expanded="showAlt">ALT</button>
<div class="ap-media__alt-text" x-show="showAlt" x-cloak @click.stop>{{ photoAlt }}</div>
{% endif %}
</div>
{% endif %}
{% endfor %}
{# Lightbox modal — teleported to body to prevent overflow clipping #}
<template x-teleport="body">
<div x-show="lightbox" x-cloak @keydown.escape.window="lightbox = false" @click.self="lightbox = false" class="ap-lightbox" role="dialog" aria-modal="true">
<button type="button" @click="lightbox = false" class="ap-lightbox__close" aria-label="Close">&times;</button>
{% if totalPhotos > 1 %}
<button type="button" @click="idx = (idx - 1 + {{ totalPhotos }}) % {{ totalPhotos }}" class="ap-lightbox__prev" aria-label="Previous image">&lsaquo;</button>
{% endif %}
<img :src="[{% for photo in item.photo %}'{{ photo.url if photo.url else photo }}'{% if not loop.last %},{% endif %}{% endfor %}][idx]"
:alt="[{% for photo in item.photo %}'{{ (photo.alt if photo.alt else '') | replace(\"'\", \"\\'\") }}'{% if not loop.last %},{% endif %}{% endfor %}][idx]"
class="ap-lightbox__img">
{% if totalPhotos > 1 %}
<button type="button" @click="idx = (idx + 1) % {{ totalPhotos }}" class="ap-lightbox__next" aria-label="Next image">&rsaquo;</button>
<div class="ap-lightbox__counter" x-text="(idx + 1) + ' / ' + {{ totalPhotos }}"></div>
{% endif %}
</div>
</template>
</div>
{% endif %}
{# Video embed #}
{% if item.video and item.video.length > 0 %}
<div class="ap-card__video">
<video controls preload="metadata" src="{{ item.video[0] }}">
{{ __("activitypub.reader.videoNotSupported") }}
</video>
</div>
{% endif %}
{# Audio player #}
{% if item.audio and item.audio.length > 0 %}
<div class="ap-card__audio">
<audio controls preload="metadata" src="{{ item.audio[0] }}">
{{ __("activitypub.reader.audioNotSupported") }}
</audio>
</div>
{% endif %}