feat: add syndication target selection to compose form

- Fetch syndication targets from Micropub config
- Display checkboxes for each target in compose form
- Include mp-syndicate-to in Micropub request
- Include optional content/comments for likes and reposts
This commit is contained in:
Ricardo
2026-02-06 20:44:53 +01:00
parent fabadd8585
commit 99cb4de4e8
4 changed files with 79 additions and 6 deletions

View File

@@ -315,6 +315,38 @@ function ensureString(value) {
return String(value);
}
/**
* Fetch syndication targets from Micropub config
* @param {object} application - Indiekit application
* @param {string} token - Auth token
* @returns {Promise<Array>} Syndication targets
*/
async function getSyndicationTargets(application, token) {
try {
const micropubEndpoint = application.micropubEndpoint;
if (!micropubEndpoint) return [];
const micropubUrl = micropubEndpoint.startsWith("http")
? micropubEndpoint
: new URL(micropubEndpoint, application.url).href;
const configUrl = `${micropubUrl}?q=config`;
const configResponse = await fetch(configUrl, {
headers: {
Authorization: `Bearer ${token}`,
Accept: "application/json",
},
});
if (!configResponse.ok) return [];
const config = await configResponse.json();
return config["syndicate-to"] || [];
} catch {
return [];
}
}
/**
* Compose response form
* @param {object} request - Express request
@@ -322,6 +354,8 @@ function ensureString(value) {
* @returns {Promise<void>}
*/
export async function compose(request, response) {
const { application } = request.app.locals;
// Support both long-form (replyTo) and short-form (reply) query params
const {
replyTo,
@@ -334,12 +368,19 @@ export async function compose(request, response) {
bookmark,
} = request.query;
// Fetch syndication targets if user is authenticated
const token = request.session?.access_token;
const syndicationTargets = token
? await getSyndicationTargets(application, token)
: [];
response.render("compose", {
title: request.__("microsub.compose.title"),
replyTo: ensureString(replyTo || reply),
likeOf: ensureString(likeOf || like),
repostOf: ensureString(repostOf || repost),
bookmarkOf: ensureString(bookmarkOf || bookmark),
syndicationTargets,
baseUrl: request.baseUrl,
});
}
@@ -357,6 +398,7 @@ export async function submitCompose(request, response) {
const likeOf = request.body["like-of"];
const repostOf = request.body["repost-of"];
const bookmarkOf = request.body["bookmark-of"];
const syndicateTo = request.body["mp-syndicate-to"];
// Debug logging
console.info(
@@ -369,6 +411,7 @@ export async function submitCompose(request, response) {
likeOf,
repostOf,
bookmarkOf,
syndicateTo,
});
// Get Micropub endpoint
@@ -396,16 +439,22 @@ export async function submitCompose(request, response) {
micropubData.append("h", "entry");
if (likeOf) {
// Like post (no content needed)
// Like post - content is optional comment
micropubData.append("like-of", likeOf);
if (content && content.trim()) {
micropubData.append("content", content.trim());
}
} else if (repostOf) {
// Repost (no content needed)
// Repost - content is optional comment
micropubData.append("repost-of", repostOf);
if (content && content.trim()) {
micropubData.append("content", content.trim());
}
} else if (bookmarkOf) {
// Bookmark (content optional)
// Bookmark - content is optional comment
micropubData.append("bookmark-of", bookmarkOf);
if (content) {
micropubData.append("content", content);
if (content && content.trim()) {
micropubData.append("content", content.trim());
}
} else if (inReplyTo) {
// Reply
@@ -416,6 +465,14 @@ export async function submitCompose(request, response) {
micropubData.append("content", content || "");
}
// Add syndication targets
if (syndicateTo) {
const targets = Array.isArray(syndicateTo) ? syndicateTo : [syndicateTo];
for (const target of targets) {
micropubData.append("mp-syndicate-to", target);
}
}
// Debug: log what we're sending
console.info("[Microsub] Sending to Micropub:", {
url: micropubUrl,

View File

@@ -45,6 +45,8 @@
"content": "What's on your mind?",
"comment": "Add a comment (optional)",
"commentHint": "Your comment will be included when this is syndicated",
"syndicateTo": "Syndicate to",
"syndicateHint": "Select where to cross-post this",
"submit": "Post",
"cancel": "Cancel",
"replyTo": "Replying to",

View File

@@ -1,6 +1,6 @@
{
"name": "@rmdes/indiekit-endpoint-microsub",
"version": "1.0.14",
"version": "1.0.15",
"description": "Microsub endpoint for Indiekit. Enables subscribing to feeds and reading content using the Microsub protocol.",
"keywords": [
"indiekit",

View File

@@ -72,6 +72,20 @@
<span id="char-count">0</span> characters
</div>
{# Syndication targets #}
{% if syndicationTargets and syndicationTargets.length %}
<fieldset class="compose__syndication">
<legend>{{ __("microsub.compose.syndicateTo") }}</legend>
<p class="hint">{{ __("microsub.compose.syndicateHint") }}</p>
{% for target in syndicationTargets %}
<label class="syndication-target">
<input type="checkbox" name="mp-syndicate-to" value="{{ target.uid }}"{% if target.checked %} checked{% endif %}>
<span class="syndication-target__name">{{ target.name }}</span>
</label>
{% endfor %}
</fieldset>
{% endif %}
<div class="button-group">
{{ button({
text: __("microsub.compose.submit")