Skip to content

Commit 8eb4d02

Browse files
MilesMorelclaude
andcommitted
Show only main pet posts in Recent posts feed
Each pet is posted to Mastodon as a thread: a main post carries the photo while caption-overflow continuation posts have no media. The website's Recent posts grid took the first 6 RSS items indiscriminately, rendering the imageless thread replies as blank placeholder cards. Filter the feed to items that have an image before slicing to the post limit, so only the main posts appear. Closes #120. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent fda08f6 commit 8eb4d02

1 file changed

Lines changed: 23 additions & 17 deletions

File tree

docs/index.html

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,20 @@ <h3>${escapeHtml(s.name)}</h3>
118118
const MEDIA_NS = "http://search.yahoo.com/mrss/";
119119
const POST_LIMIT = 6;
120120

121+
// First image attachment on an RSS <item>, or "" if none. Caption-overflow
122+
// thread replies carry no media, so this also tells main posts from replies.
123+
const itemImageUrl = (item) => {
124+
const mediaElements = item.getElementsByTagNameNS(MEDIA_NS, "content");
125+
for (const mediaElement of mediaElements) {
126+
const medium = mediaElement.getAttribute("medium");
127+
if (!medium || medium === "image") {
128+
const url = mediaElement.getAttribute("url") || "";
129+
if (url) return url;
130+
}
131+
}
132+
return "";
133+
};
134+
121135
fetch("https://mastodon.social/@cutepetsboston.rss")
122136
.then((r) => {
123137
if (!r.ok) throw new Error("RSS request failed: " + r.status);
@@ -128,14 +142,17 @@ <h3>${escapeHtml(s.name)}</h3>
128142
if (doc.querySelector("parsererror")) {
129143
throw new Error("RSS parse error");
130144
}
131-
const items = Array.from(doc.querySelectorAll("item")).slice(0, POST_LIMIT);
145+
const posts = Array.from(doc.querySelectorAll("item"))
146+
.map((item) => ({ item, imageUrl: itemImageUrl(item) }))
147+
.filter(({ imageUrl }) => imageUrl)
148+
.slice(0, POST_LIMIT);
132149
const grid = document.getElementById("post-grid");
133-
if (items.length === 0) {
150+
if (posts.length === 0) {
134151
grid.innerHTML = '<p class="dashboard-note">No recent posts found.</p>';
135152
return;
136153
}
137-
grid.innerHTML = items
138-
.map((item) => {
154+
grid.innerHTML = posts
155+
.map(({ item, imageUrl }) => {
139156
const link = item.querySelector("link")?.textContent || "#";
140157
const pubDate = item.querySelector("pubDate")?.textContent || "";
141158
const dateStr = pubDate
@@ -149,21 +166,10 @@ <h3>${escapeHtml(s.name)}</h3>
149166
tmp.innerHTML = descHtml;
150167
const text = (tmp.textContent || "").trim();
151168
const caption = text.length > 140 ? text.slice(0, 137) + "…" : text;
152-
const mediaEls = item.getElementsByTagNameNS(MEDIA_NS, "content");
153-
let imgUrl = "";
154-
for (const el of mediaEls) {
155-
const medium = el.getAttribute("medium");
156-
if (!medium || medium === "image") {
157-
imgUrl = el.getAttribute("url") || "";
158-
if (imgUrl) break;
159-
}
160-
}
161-
const img = imgUrl
162-
? `<img class="post-image" src="${escapeHtml(imgUrl)}" alt="" loading="lazy" />`
163-
: '<div class="post-image post-image-placeholder"></div>';
169+
const imageTag = `<img class="post-image" src="${escapeHtml(imageUrl)}" alt="" loading="lazy" />`;
164170
return `
165171
<a class="post-card" href="${escapeHtml(link)}" target="_blank" rel="noopener">
166-
${img}
172+
${imageTag}
167173
<div class="post-body">
168174
<div class="post-caption">${escapeHtml(caption) || "View post"}</div>
169175
<div class="meta">${escapeHtml(dateStr)}</div>

0 commit comments

Comments
 (0)