fix(tags): normalize nested category paths for ActivityPub hashtags
Nested categories like `on/tech` or `art/music` were being sent as-is to ActivityPub consumers. Three fixes: - status.js (Mastodon Client API): raw category used as tag name/URL instead of the normalized last segment - jf2-to-as2.js buildPlainTags/buildFedifyTags: href used encodeURIComponent() on the full path, encoding the `/` separator and producing broken category URLs (e.g. `categories/on%2Ftech` instead of `categories/on/tech`) Tag names are now consistently normalized to the last path segment (`on/tech` → `#tech`). Category hrefs encode each segment individually but preserve the `/` separator. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+8
-6
@@ -621,10 +621,12 @@ function buildPlainTags(properties, publicationUrl, existing) {
|
||||
const tags = [...(existing || [])];
|
||||
if (properties.category) {
|
||||
for (const cat of asArray(properties.category)) {
|
||||
const normalized = cat.split("/").at(-1).replace(/\s+/g, "");
|
||||
const segments = cat.split("/").map((s) => encodeURIComponent(s.replace(/\s+/g, "")));
|
||||
tags.push({
|
||||
type: "Hashtag",
|
||||
name: `#${cat.split("/").at(-1).replace(/\s+/g, "")}`,
|
||||
href: `${publicationUrl}categories/${encodeURIComponent(cat)}`,
|
||||
name: `#${normalized}`,
|
||||
href: `${publicationUrl}categories/${segments.join("/")}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -643,12 +645,12 @@ function buildFedifyTags(properties, publicationUrl, postType) {
|
||||
}
|
||||
if (properties.category) {
|
||||
for (const cat of asArray(properties.category)) {
|
||||
const normalized = cat.split("/").at(-1).replace(/\s+/g, "");
|
||||
const segments = cat.split("/").map((s) => encodeURIComponent(s.replace(/\s+/g, "")));
|
||||
tags.push(
|
||||
new Hashtag({
|
||||
name: `#${cat.split("/").at(-1).replace(/\s+/g, "")}`,
|
||||
href: new URL(
|
||||
`${publicationUrl}categories/${encodeURIComponent(cat)}`,
|
||||
),
|
||||
name: `#${normalized}`,
|
||||
href: new URL(`${publicationUrl}categories/${segments.join("/")}`),
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -173,11 +173,14 @@ export function serializeStatus(item, { baseUrl, favouritedIds, rebloggedIds, bo
|
||||
// Link preview -> card
|
||||
const card = serializeCard(item.linkPreviews?.[0]);
|
||||
|
||||
// Tags from category[]
|
||||
const tags = (item.category || []).map((tag) => ({
|
||||
name: tag,
|
||||
url: `${baseUrl}/tags/${encodeURIComponent(tag)}`,
|
||||
}));
|
||||
// Tags from category[] — normalize nested paths (e.g. "on/tech" → "tech")
|
||||
const tags = (item.category || []).map((tag) => {
|
||||
const normalized = tag.split("/").at(-1).replace(/\s+/g, "");
|
||||
return {
|
||||
name: normalized,
|
||||
url: `${baseUrl}/tags/${encodeURIComponent(normalized)}`,
|
||||
};
|
||||
});
|
||||
|
||||
// Mentions — use actorUrl for deterministic ID, parse acct from handle
|
||||
const mentions = (item.mentions || []).map((m) => {
|
||||
|
||||
Reference in New Issue
Block a user