@rmdes/indiekit-endpoint-activitypub 2.4.2 → 2.4.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -282,7 +282,9 @@ document.addEventListener("alpine:init", () => {
282
282
  const card = entry.target;
283
283
  const uid = card.dataset.uid;
284
284
  if (uid && !card.classList.contains("ap-card--read")) {
285
- card.classList.add("ap-card--read");
285
+ // Mark read server-side but DON'T dim visually in this session.
286
+ // Cards only appear dimmed when they arrive from the server
287
+ // with item.read=true on a subsequent page load.
286
288
  this._batch.push(uid);
287
289
  }
288
290
  this._observer.unobserve(card);
@@ -92,7 +92,7 @@ export function mapMastodonStatusToItem(status, instance) {
92
92
  }
93
93
  }
94
94
 
95
- return {
95
+ const item = {
96
96
  uid: status.url || status.uri || "",
97
97
  url: status.url || status.uri || "",
98
98
  type: "note",
@@ -119,4 +119,45 @@ export function mapMastodonStatusToItem(status, instance) {
119
119
  createdAt: new Date().toISOString(),
120
120
  _explore: true,
121
121
  };
122
+
123
+ // Map quoted post data if present (Mastodon 4.3+ quote support)
124
+ // Mastodon API wraps the quoted status: { state: "accepted", quoted_status: { ...fullStatus } }
125
+ const quotedStatus = status.quote?.quoted_status || null;
126
+ if (quotedStatus) {
127
+ item.quoteUrl = quotedStatus.url || quotedStatus.uri || "";
128
+
129
+ const q = quotedStatus;
130
+ const qAccount = q.account || {};
131
+ const qAcct = qAccount.acct || "";
132
+ const qHandle = qAcct.includes("@") ? `@${qAcct}` : `@${qAcct}@${instance}`;
133
+ const qPhoto = [];
134
+ for (const att of q.media_attachments || []) {
135
+ const attUrl = att.url || att.remote_url || "";
136
+ if (attUrl && (att.type === "image" || att.type === "gifv")) {
137
+ qPhoto.push(attUrl);
138
+ }
139
+ }
140
+
141
+ item.quote = {
142
+ url: q.url || q.uri || "",
143
+ uid: q.uri || q.url || "",
144
+ author: {
145
+ name: sanitizeHtml(qAccount.display_name || qAccount.username || "Unknown", { allowedTags: [], allowedAttributes: {} }),
146
+ url: qAccount.url || "",
147
+ photo: qAccount.avatar || qAccount.avatar_static || "",
148
+ handle: qHandle,
149
+ },
150
+ content: {
151
+ text: (q.content || "").replace(/<[^>]*>/g, ""),
152
+ html: sanitizeContent(q.content || ""),
153
+ },
154
+ published: q.created_at || "",
155
+ name: "",
156
+ photo: qPhoto.slice(0, 1),
157
+ };
158
+ } else {
159
+ item.quoteUrl = "";
160
+ }
161
+
162
+ return item;
122
163
  }
@@ -8,6 +8,7 @@
8
8
  import { searchInstances, checkInstanceTimeline, getPopularAccounts } from "../fedidb.js";
9
9
  import { getToken } from "../csrf.js";
10
10
  import { validateInstance, validateHashtag, mapMastodonStatusToItem } from "./explore-utils.js";
11
+ import { stripQuoteReferenceHtml } from "../og-unfurl.js";
11
12
 
12
13
  const FETCH_TIMEOUT_MS = 10_000;
13
14
  const MAX_RESULTS = 20;
@@ -98,6 +99,14 @@ export function exploreController(mountPath) {
98
99
 
99
100
  items = statuses.map((s) => mapMastodonStatusToItem(s, instance));
100
101
 
102
+ // Strip "RE:" paragraphs from items that have quote embeds
103
+ for (const item of items) {
104
+ const quoteRef = item.quoteUrl || item.quote?.url || item.quote?.uid;
105
+ if (item.quote && quoteRef && item.content?.html) {
106
+ item.content.html = stripQuoteReferenceHtml(item.content.html, quoteRef);
107
+ }
108
+ }
109
+
101
110
  // Get next max_id from last item for pagination
102
111
  if (statuses.length === MAX_RESULTS && statuses.length > 0) {
103
112
  const last = statuses[statuses.length - 1];
@@ -181,6 +190,14 @@ export function exploreApiController(mountPath) {
181
190
 
182
191
  const items = statuses.map((s) => mapMastodonStatusToItem(s, instance));
183
192
 
193
+ // Strip "RE:" paragraphs from items that have quote embeds
194
+ for (const item of items) {
195
+ const quoteRef = item.quoteUrl || item.quote?.url || item.quote?.uid;
196
+ if (item.quote && quoteRef && item.content?.html) {
197
+ item.content.html = stripQuoteReferenceHtml(item.content.html, quoteRef);
198
+ }
199
+ }
200
+
184
201
  let nextMaxId = null;
185
202
  if (statuses.length === MAX_RESULTS && statuses.length > 0) {
186
203
  const last = statuses[statuses.length - 1];
@@ -18,6 +18,7 @@
18
18
 
19
19
  import { validateHashtag, mapMastodonStatusToItem } from "./explore-utils.js";
20
20
  import { getToken } from "../csrf.js";
21
+ import { stripQuoteReferenceHtml } from "../og-unfurl.js";
21
22
 
22
23
  const FETCH_TIMEOUT_MS = 10_000;
23
24
  const PAGE_SIZE = 20;
@@ -183,6 +184,14 @@ export function hashtagExploreApiController(mountPath) {
183
184
  mapMastodonStatusToItem(status, domain)
184
185
  );
185
186
 
187
+ // Strip "RE:" paragraphs from items that have quote embeds
188
+ for (const item of items) {
189
+ const quoteRef = item.quoteUrl || item.quote?.url || item.quote?.uid;
190
+ if (item.quote && quoteRef && item.content?.html) {
191
+ item.content.html = stripQuoteReferenceHtml(item.content.html, quoteRef);
192
+ }
193
+ }
194
+
186
195
  // Render HTML AFTER merge/dedup/paginate (don't waste CPU on discarded items)
187
196
  const csrfToken = getToken(request.session);
188
197
  const templateData = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rmdes/indiekit-endpoint-activitypub",
3
- "version": "2.4.2",
3
+ "version": "2.4.4",
4
4
  "description": "ActivityPub federation endpoint for Indiekit via Fedify. Adds full fediverse support: actor, inbox, outbox, followers, following, syndication, and Mastodon migration.",
5
5
  "keywords": [
6
6
  "indiekit",