fix: use async iteration for Fedify 2.0 attachments/tags, add image lightbox
Fedify 2.0's getAttachments() and getTags() return async iterables, but the code used synchronous for...of which silently yielded zero results. Changed to for await...of so media URLs (photo/video/audio) and hashtags are now properly extracted from incoming posts. Also replaced the gallery's target=_blank links with an Alpine.js lightbox modal for full-size image viewing with prev/next navigation and keyboard support.
This commit is contained in:
@@ -457,7 +457,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.ap-card__gallery-link {
|
.ap-card__gallery-link {
|
||||||
|
appearance: none;
|
||||||
|
background: none;
|
||||||
|
border: 0;
|
||||||
|
cursor: pointer;
|
||||||
display: block;
|
display: block;
|
||||||
|
padding: 0;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -522,6 +527,83 @@
|
|||||||
grid-template-rows: 1fr 1fr;
|
grid-template-rows: 1fr 1fr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ==========================================================================
|
||||||
|
Photo Lightbox
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
[x-cloak] {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ap-lightbox {
|
||||||
|
align-items: center;
|
||||||
|
background: rgba(0, 0, 0, 0.92);
|
||||||
|
display: flex;
|
||||||
|
inset: 0;
|
||||||
|
justify-content: center;
|
||||||
|
position: fixed;
|
||||||
|
z-index: 9999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ap-lightbox__img {
|
||||||
|
max-height: 90vh;
|
||||||
|
max-width: 95vw;
|
||||||
|
object-fit: contain;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ap-lightbox__close {
|
||||||
|
background: none;
|
||||||
|
border: 0;
|
||||||
|
color: white;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 2rem;
|
||||||
|
line-height: 1;
|
||||||
|
padding: var(--space-s);
|
||||||
|
position: absolute;
|
||||||
|
right: var(--space-m);
|
||||||
|
top: var(--space-m);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ap-lightbox__close:hover {
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ap-lightbox__prev,
|
||||||
|
.ap-lightbox__next {
|
||||||
|
background: none;
|
||||||
|
border: 0;
|
||||||
|
color: white;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 3rem;
|
||||||
|
line-height: 1;
|
||||||
|
padding: var(--space-m);
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ap-lightbox__prev {
|
||||||
|
left: var(--space-s);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ap-lightbox__next {
|
||||||
|
right: var(--space-s);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ap-lightbox__prev:hover,
|
||||||
|
.ap-lightbox__next:hover {
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ap-lightbox__counter {
|
||||||
|
bottom: var(--space-m);
|
||||||
|
color: white;
|
||||||
|
font-size: var(--font-size-s);
|
||||||
|
left: 50%;
|
||||||
|
position: absolute;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
}
|
||||||
|
|
||||||
/* ==========================================================================
|
/* ==========================================================================
|
||||||
Video Embed
|
Video Embed
|
||||||
========================================================================== */
|
========================================================================== */
|
||||||
|
|||||||
@@ -185,7 +185,7 @@ export async function extractObjectData(object, options = {}) {
|
|||||||
try {
|
try {
|
||||||
if (typeof object.getTags === "function") {
|
if (typeof object.getTags === "function") {
|
||||||
const tags = await object.getTags();
|
const tags = await object.getTags();
|
||||||
for (const tag of tags) {
|
for await (const tag of tags) {
|
||||||
if (tag.name) {
|
if (tag.name) {
|
||||||
const tagName = tag.name.toString().replace(/^#/, "");
|
const tagName = tag.name.toString().replace(/^#/, "");
|
||||||
if (tagName) category.push(tagName);
|
if (tagName) category.push(tagName);
|
||||||
@@ -204,7 +204,7 @@ export async function extractObjectData(object, options = {}) {
|
|||||||
try {
|
try {
|
||||||
if (typeof object.getAttachments === "function") {
|
if (typeof object.getAttachments === "function") {
|
||||||
const attachments = await object.getAttachments();
|
const attachments = await object.getAttachments();
|
||||||
for (const att of attachments) {
|
for await (const att of attachments) {
|
||||||
const mediaUrl = att.url?.href || "";
|
const mediaUrl = att.url?.href || "";
|
||||||
if (!mediaUrl) continue;
|
if (!mediaUrl) continue;
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@rmdes/indiekit-endpoint-activitypub",
|
"name": "@rmdes/indiekit-endpoint-activitypub",
|
||||||
"version": "2.0.16",
|
"version": "2.0.17",
|
||||||
"description": "ActivityPub federation endpoint for Indiekit via Fedify. Adds full fediverse support: actor, inbox, outbox, followers, following, syndication, and Mastodon migration.",
|
"description": "ActivityPub federation endpoint for Indiekit via Fedify. Adds full fediverse support: actor, inbox, outbox, followers, following, syndication, and Mastodon migration.",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"indiekit",
|
"indiekit",
|
||||||
|
|||||||
@@ -1,20 +1,36 @@
|
|||||||
{# Media attachments partial — included from ap-item-card.njk #}
|
{# Media attachments partial — included from ap-item-card.njk #}
|
||||||
|
|
||||||
{# Photo gallery #}
|
{# Photo gallery with lightbox #}
|
||||||
{% if item.photo and item.photo.length > 0 %}
|
{% if item.photo and item.photo.length > 0 %}
|
||||||
{% set displayCount = [item.photo.length, 4] | min %}
|
{% set displayCount = [item.photo.length, 4] | min %}
|
||||||
{% set extraCount = item.photo.length - 4 %}
|
{% set extraCount = item.photo.length - 4 %}
|
||||||
<div class="ap-card__gallery ap-card__gallery--{{ displayCount }}">
|
{% set totalPhotos = item.photo.length %}
|
||||||
|
<div x-data="{ lightbox: false, idx: 0 }" class="ap-card__gallery ap-card__gallery--{{ displayCount }}">
|
||||||
{% for photoUrl in item.photo %}
|
{% for photoUrl in item.photo %}
|
||||||
{% if loop.index0 < 4 %}
|
{% if loop.index0 < 4 %}
|
||||||
<a href="{{ photoUrl }}" target="_blank" rel="noopener" class="ap-card__gallery-link{% if loop.index0 == 3 and extraCount > 0 %} ap-card__gallery-link--more{% endif %}">
|
<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="{{ photoUrl }}" alt="" loading="lazy">
|
<img src="{{ photoUrl }}" alt="" loading="lazy">
|
||||||
{% if loop.index0 == 3 and extraCount > 0 %}
|
{% if loop.index0 == 3 and extraCount > 0 %}
|
||||||
<span class="ap-card__gallery-more">+{{ extraCount }}</span>
|
<span class="ap-card__gallery-more">+{{ extraCount }}</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</a>
|
</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% 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">×</button>
|
||||||
|
{% if totalPhotos > 1 %}
|
||||||
|
<button type="button" @click="idx = (idx - 1 + {{ totalPhotos }}) % {{ totalPhotos }}" class="ap-lightbox__prev" aria-label="Previous image">‹</button>
|
||||||
|
{% endif %}
|
||||||
|
<img :src="[{% for p in item.photo %}'{{ p }}'{% if not loop.last %},{% endif %}{% endfor %}][idx]" class="ap-lightbox__img" alt="">
|
||||||
|
{% if totalPhotos > 1 %}
|
||||||
|
<button type="button" @click="idx = (idx + 1) % {{ totalPhotos }}" class="ap-lightbox__next" aria-label="Next image">›</button>
|
||||||
|
<div class="ap-lightbox__counter" x-text="(idx + 1) + ' / ' + {{ totalPhotos }}"></div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user