Two bugs caused replies-to-replies to be posted as 'note' type without ActivityPub federation: 1. patch-ap-compose-default-checked: The AP reader compose form had defaultChecked hardcoded to '@rick@rmendes.net' (original dev's handle), so the AP syndication checkbox was never pre-checked. Fixed to use target.checked from the Micropub q=config response, which already carries checked: true for the AP syndicator. 2. patch-ap-mastodon-reply-threading: POST /api/v1/statuses deferred ap_timeline insertion until the Eleventy build webhook fired (30–120 s). If the user replied to their own new post before the build finished, findTimelineItemById returned null → inReplyTo = null → no in-reply-to in JF2 → post-type-discovery returned 'note' → reply saved at /notes/ and sent without inReplyTo in the AP activity, breaking thread display on remote servers. Fixed by eagerly inserting the provisional timeline item immediately after postContent.create() ($setOnInsert — idempotent; syndicator upsert later is a no-op). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
91 lines
3.1 KiB
JavaScript
91 lines
3.1 KiB
JavaScript
/**
|
|
* Patch: fix hardcoded defaultChecked handles in AP reader compose controller.
|
|
*
|
|
* Root cause:
|
|
* composeController() in compose.js sets target.defaultChecked using a
|
|
* hardcoded name comparison:
|
|
*
|
|
* target.defaultChecked = name === "@rick@rmendes.net" || name === "@rmendes.net";
|
|
*
|
|
* These are the original developer's handles and will never match any target
|
|
* on this installation. As a result, ALL syndication checkboxes in the AP
|
|
* reader compose form are rendered unchecked, so replies composed through the
|
|
* AP reader are never syndicated to ActivityPub.
|
|
*
|
|
* Fix:
|
|
* Replace the hardcoded comparison with `target.checked === true`.
|
|
* The Micropub config endpoint (q=config) already returns each syndicator's
|
|
* `checked` state. The AP syndicator has `checked: true` in indiekit.config.mjs,
|
|
* so the AP checkbox will be pre-checked by default, matching the same behaviour
|
|
* as the microsub reader compose form.
|
|
*/
|
|
|
|
import { access, readFile, writeFile } from "node:fs/promises";
|
|
|
|
const MARKER = "// [patch] ap-compose-default-checked";
|
|
|
|
const candidates = [
|
|
"node_modules/@rmdes/indiekit-endpoint-activitypub/lib/controllers/compose.js",
|
|
"node_modules/@indiekit/indiekit/node_modules/@rmdes/indiekit-endpoint-activitypub/lib/controllers/compose.js",
|
|
];
|
|
|
|
const OLD_SNIPPET = ` // Default-check only AP (Fedify) and Bluesky targets
|
|
// "@rick@rmendes.net" = AP Fedify, "@rmendes.net" = Bluesky
|
|
for (const target of syndicationTargets) {
|
|
const name = target.name || "";
|
|
target.defaultChecked = name === "@rick@rmendes.net" || name === "@rmendes.net";
|
|
}`;
|
|
|
|
const NEW_SNIPPET = ` // Pre-check syndication targets based on their configured checked state ${MARKER}
|
|
for (const target of syndicationTargets) { ${MARKER}
|
|
target.defaultChecked = target.checked === true; ${MARKER}
|
|
} ${MARKER}`;
|
|
|
|
async function exists(filePath) {
|
|
try {
|
|
await access(filePath);
|
|
return true;
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
let checked = 0;
|
|
let patched = 0;
|
|
|
|
for (const filePath of candidates) {
|
|
if (!(await exists(filePath))) continue;
|
|
checked += 1;
|
|
|
|
const source = await readFile(filePath, "utf8");
|
|
|
|
if (source.includes(MARKER)) {
|
|
console.log(`[postinstall] patch-ap-compose-default-checked: already applied to ${filePath}`);
|
|
continue;
|
|
}
|
|
|
|
if (!source.includes(OLD_SNIPPET)) {
|
|
console.warn(`[postinstall] patch-ap-compose-default-checked: target snippet not found in ${filePath}`);
|
|
continue;
|
|
}
|
|
|
|
const updated = source.replace(OLD_SNIPPET, NEW_SNIPPET);
|
|
|
|
if (updated === source) {
|
|
console.log(`[postinstall] patch-ap-compose-default-checked: no changes in ${filePath}`);
|
|
continue;
|
|
}
|
|
|
|
await writeFile(filePath, updated, "utf8");
|
|
patched += 1;
|
|
console.log(`[postinstall] Applied patch-ap-compose-default-checked to ${filePath}`);
|
|
}
|
|
|
|
if (checked === 0) {
|
|
console.log("[postinstall] patch-ap-compose-default-checked: no target files found");
|
|
} else if (patched === 0) {
|
|
console.log("[postinstall] patch-ap-compose-default-checked: already up to date");
|
|
} else {
|
|
console.log(`[postinstall] patch-ap-compose-default-checked: patched ${patched} file(s)`);
|
|
}
|