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:
@@ -315,6 +315,38 @@ function ensureString(value) {
|
|||||||
return String(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
|
* Compose response form
|
||||||
* @param {object} request - Express request
|
* @param {object} request - Express request
|
||||||
@@ -322,6 +354,8 @@ function ensureString(value) {
|
|||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
export async function compose(request, response) {
|
export async function compose(request, response) {
|
||||||
|
const { application } = request.app.locals;
|
||||||
|
|
||||||
// Support both long-form (replyTo) and short-form (reply) query params
|
// Support both long-form (replyTo) and short-form (reply) query params
|
||||||
const {
|
const {
|
||||||
replyTo,
|
replyTo,
|
||||||
@@ -334,12 +368,19 @@ export async function compose(request, response) {
|
|||||||
bookmark,
|
bookmark,
|
||||||
} = request.query;
|
} = 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", {
|
response.render("compose", {
|
||||||
title: request.__("microsub.compose.title"),
|
title: request.__("microsub.compose.title"),
|
||||||
replyTo: ensureString(replyTo || reply),
|
replyTo: ensureString(replyTo || reply),
|
||||||
likeOf: ensureString(likeOf || like),
|
likeOf: ensureString(likeOf || like),
|
||||||
repostOf: ensureString(repostOf || repost),
|
repostOf: ensureString(repostOf || repost),
|
||||||
bookmarkOf: ensureString(bookmarkOf || bookmark),
|
bookmarkOf: ensureString(bookmarkOf || bookmark),
|
||||||
|
syndicationTargets,
|
||||||
baseUrl: request.baseUrl,
|
baseUrl: request.baseUrl,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -357,6 +398,7 @@ export async function submitCompose(request, response) {
|
|||||||
const likeOf = request.body["like-of"];
|
const likeOf = request.body["like-of"];
|
||||||
const repostOf = request.body["repost-of"];
|
const repostOf = request.body["repost-of"];
|
||||||
const bookmarkOf = request.body["bookmark-of"];
|
const bookmarkOf = request.body["bookmark-of"];
|
||||||
|
const syndicateTo = request.body["mp-syndicate-to"];
|
||||||
|
|
||||||
// Debug logging
|
// Debug logging
|
||||||
console.info(
|
console.info(
|
||||||
@@ -369,6 +411,7 @@ export async function submitCompose(request, response) {
|
|||||||
likeOf,
|
likeOf,
|
||||||
repostOf,
|
repostOf,
|
||||||
bookmarkOf,
|
bookmarkOf,
|
||||||
|
syndicateTo,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Get Micropub endpoint
|
// Get Micropub endpoint
|
||||||
@@ -396,16 +439,22 @@ export async function submitCompose(request, response) {
|
|||||||
micropubData.append("h", "entry");
|
micropubData.append("h", "entry");
|
||||||
|
|
||||||
if (likeOf) {
|
if (likeOf) {
|
||||||
// Like post (no content needed)
|
// Like post - content is optional comment
|
||||||
micropubData.append("like-of", likeOf);
|
micropubData.append("like-of", likeOf);
|
||||||
|
if (content && content.trim()) {
|
||||||
|
micropubData.append("content", content.trim());
|
||||||
|
}
|
||||||
} else if (repostOf) {
|
} else if (repostOf) {
|
||||||
// Repost (no content needed)
|
// Repost - content is optional comment
|
||||||
micropubData.append("repost-of", repostOf);
|
micropubData.append("repost-of", repostOf);
|
||||||
|
if (content && content.trim()) {
|
||||||
|
micropubData.append("content", content.trim());
|
||||||
|
}
|
||||||
} else if (bookmarkOf) {
|
} else if (bookmarkOf) {
|
||||||
// Bookmark (content optional)
|
// Bookmark - content is optional comment
|
||||||
micropubData.append("bookmark-of", bookmarkOf);
|
micropubData.append("bookmark-of", bookmarkOf);
|
||||||
if (content) {
|
if (content && content.trim()) {
|
||||||
micropubData.append("content", content);
|
micropubData.append("content", content.trim());
|
||||||
}
|
}
|
||||||
} else if (inReplyTo) {
|
} else if (inReplyTo) {
|
||||||
// Reply
|
// Reply
|
||||||
@@ -416,6 +465,14 @@ export async function submitCompose(request, response) {
|
|||||||
micropubData.append("content", content || "");
|
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
|
// Debug: log what we're sending
|
||||||
console.info("[Microsub] Sending to Micropub:", {
|
console.info("[Microsub] Sending to Micropub:", {
|
||||||
url: micropubUrl,
|
url: micropubUrl,
|
||||||
|
|||||||
@@ -45,6 +45,8 @@
|
|||||||
"content": "What's on your mind?",
|
"content": "What's on your mind?",
|
||||||
"comment": "Add a comment (optional)",
|
"comment": "Add a comment (optional)",
|
||||||
"commentHint": "Your comment will be included when this is syndicated",
|
"commentHint": "Your comment will be included when this is syndicated",
|
||||||
|
"syndicateTo": "Syndicate to",
|
||||||
|
"syndicateHint": "Select where to cross-post this",
|
||||||
"submit": "Post",
|
"submit": "Post",
|
||||||
"cancel": "Cancel",
|
"cancel": "Cancel",
|
||||||
"replyTo": "Replying to",
|
"replyTo": "Replying to",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@rmdes/indiekit-endpoint-microsub",
|
"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.",
|
"description": "Microsub endpoint for Indiekit. Enables subscribing to feeds and reading content using the Microsub protocol.",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"indiekit",
|
"indiekit",
|
||||||
|
|||||||
@@ -72,6 +72,20 @@
|
|||||||
<span id="char-count">0</span> characters
|
<span id="char-count">0</span> characters
|
||||||
</div>
|
</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">
|
<div class="button-group">
|
||||||
{{ button({
|
{{ button({
|
||||||
text: __("microsub.compose.submit")
|
text: __("microsub.compose.submit")
|
||||||
|
|||||||
Reference in New Issue
Block a user