Changeset e4119c2 in Klonkt
- Timestamp:
- 10/29/2026 05:35:25 PM (2 hours ago)
- Branches:
- main
- Children:
- 3155d8c
- Parents:
- 7ccdced
- Files:
-
- 1 added
- 3 edited
-
src/routes/activitypub.js (modified) (2 diffs)
-
src/services/ActivityPubService.js (modified) (3 diffs)
-
src/services/ap-timeline.js (modified) (1 diff)
-
test/inbox-ouder.test.js (added)
Legend:
- Unmodified
- Added
- Removed
-
src/routes/activitypub.js
r7ccdced re4119c2 965 965 }, 'private, no-store'); 966 966 } 967 // ── Ouder dan wat je hebt (shaer-ewrt) ────────────────────────── 968 // 969 // De volle lezing hieronder is vier poten van de nieuwste zestig, en wat 970 // daaronder lag was nergens te vragen: een 'load more' had geen adres. Dit is 971 // dat adres, met een samengestelde cursor zoals bij /messages, en het antwoord 972 // is een OrderedCollectionPage met partOf -- een deel, en dat zegt het ook. 973 // 974 // De pagina's volgen elkaar via `next`; de client bouwt er geen na. 975 const inboxId = `${base}/ap/users/${auth.site.slug}/inbox`; 976 const size = req.query.limit ? `&limit=${encodeURIComponent(String(req.query.limit))}` : ''; 977 const ouderLink = (pagina) => (pagina.more && pagina.oldest 978 ? `${inboxId}?before=${encodeURIComponent(pagina.oldest)}${size}` : undefined); 979 if (req.query.before) { 980 const pagina = AP.inboxPage(auth.site.slug, { before: String(req.query.before), limit: req.query.limit }); 981 const ids = (leg) => pagina.rows.filter((r) => r.leg === leg).map((r) => r.ref); 982 const tl = new Map(AP.timelineRowsByIds(auth.site.slug, ids('timeline')).map((r) => [r.id, r])); 983 const mn = new Map(AP.messageRowsByUri(auth.site.slug, ids('message')).map((r) => [r.object_uri, r])); 984 const rp = new Map(AP.replyRowsByUri(auth.site.slug, ids('reply')).map((r) => [r.object_uri, r])); 985 const reacties = AP.getReactionsFor(auth.site.slug, [...tl.keys()]); 986 const ctx = { base, me, myHandle, p: P }; 987 const mine = AP.selfAuthor(base, auth.site); 988 const items = []; 989 for (const r of pagina.rows) { 990 if (r.leg === 'timeline') { 991 const t = tl.get(r.ref); 992 if (t) items.push(timelineItem(t, { p: P, reactions: reacties })); 993 } else if (r.leg === 'message') { 994 // Dezelfde poort als de volle lezing: een dichte berichtenpoort houdt 995 // vreemden buiten, nooit de hulpvraag en nooit de guardians. 996 const m = mn.get(r.ref); 997 if (m && (messagesAllowed || m.help_request || guardianUris.has(m.actor_uri))) items.push(messageItem(m, ctx)); 998 } else if (r.leg === 'reply') { 999 const m = rp.get(r.ref); 1000 if (m) items.push(replyItem(m, ctx)); 1001 } else { 1002 const n = AP.getOutboxNote(base, r.ref); 1003 if (n) items.push(sentItem(n, { me, mine })); 1004 } 1005 } 1006 return AP.sendAP(res, { 1007 '@context': AP.AP_CONTEXT, 1008 id: `${inboxId}?before=${encodeURIComponent(String(req.query.before))}${size}`, 1009 type: 'OrderedCollectionPage', 1010 partOf: inboxId, 1011 orderedItems: items, 1012 // Ontbreekt next, dan is de inbox op. De client hoort dat te weten zonder 1013 // te gokken, anders kan zijn knop niet eerlijk verdwijnen. 1014 next: ouderLink(pagina), 1015 }, 'private, no-store'); 1016 } 967 1017 const rows = AP.getTimeline(auth.site.slug, 60); 968 1018 // Eén query voor de hele pagina (shaer-9e9 fase 2): shaer:liked komt uit de … … 1021 1071 totalItems: items.length, 1022 1072 orderedItems: items, 1073 // Waar het oudere begint (shaer-ewrt). Een `next` op de wortel en geen 1074 // `first`, en dat is een afweging: de items hierboven ZIJN de eerste 1075 // pagina, en een `first` ernaast laat een lezer die de paginaweg voorrang 1076 // geeft -- de onze, op iOS en op Android -- bij elke verversing de hele 1077 // inbox opnieuw in pagina's binnenhalen. `next` volgt niemand vanzelf; hij 1078 // is er voor wie erom vraagt. 1079 // 1080 // De cursor komt uit dezelfde unie als de pagina's, over de nieuwste zestig 1081 // van alle poten samen. Die zitten hier allemaal tussen, dus de volgende 1082 // pagina sluit aan zonder gat; wat de wortel per poot dieper had komt daar 1083 // hooguit nog een keer langs. 1084 next: ouderLink(AP.inboxPage(auth.site.slug, { limit: req.query.limit || 60 })), 1023 1085 }); 1024 1086 return undefined; -
src/services/ActivityPubService.js
r7ccdced re4119c2 55 55 getTimeline, replyRowsByUri, timelineRowsByIds, getReplyMessages, 56 56 feedCursor, feedChangesSince, waitForFeedChange, 57 conversationHeads, conversationHistory, messageRowsByUri,57 conversationHeads, conversationHistory, inboxPage, messageRowsByUri, 58 58 readMarkers, markRead, unreadPerConversation, getDirectMessages, 59 59 isoStamp, timelineAttachments, extractEmojiTags, gateAttachments, … … 64 64 getTimeline, replyRowsByUri, timelineRowsByIds, getReplyMessages, 65 65 feedCursor, feedChangesSince, waitForFeedChange, 66 conversationHeads, conversationHistory, messageRowsByUri,66 conversationHeads, conversationHistory, inboxPage, messageRowsByUri, 67 67 readMarkers, markRead, unreadPerConversation, getDirectMessages, 68 68 isoStamp, timelineAttachments, extractEmojiTags, gateAttachments, … … 4395 4395 getInteractions, getInteractionById, setInteractionBoosted, setInteractionLiked, buildReplyNote, getOutboxNote, getSentNotes, deliverReply, resolveRemoteNote, noteAudience, mayReadNote, 4396 4396 listOutbox, deliverOutboxDelete, deliverOutboxUpdate, deliverDirectNote, 4397 webfingerResolve, followActor, resolveRemoteActor, unfollowActor, handleMoveInbox, moveAccount, listFollowing, setAutoBoost, backfillFromOutbox, getTimeline, timelineRowsByIds, contentWarning, getDirectMessages, readMarkers, markRead, unreadPerConversation, messageRowsByUri, replyRowsByUri, conversationHeads, conversationHistory, i soStamp, timelineAttachments, timelineEmojis, timelineObjectLinks, timelineQuote, timelineEmbed, applyQuoteProps, deliverToActor, sendInteraction, voteOnPoll, voteOnRemotePoll,4397 webfingerResolve, followActor, resolveRemoteActor, unfollowActor, handleMoveInbox, moveAccount, listFollowing, setAutoBoost, backfillFromOutbox, getTimeline, timelineRowsByIds, contentWarning, getDirectMessages, readMarkers, markRead, unreadPerConversation, messageRowsByUri, replyRowsByUri, conversationHeads, conversationHistory, inboxPage, isoStamp, timelineAttachments, timelineEmojis, timelineObjectLinks, timelineQuote, timelineEmbed, applyQuoteProps, deliverToActor, sendInteraction, voteOnPoll, voteOnRemotePoll, 4398 4398 acceptGatedFollow, rejectGatedFollow, isWardGuardian, outboxAudience, sendFollowDecision, 4399 4399 gateOutgoingFollow, performApprovedFollow, recordGuardianEvent, listGuardianEvents, GUARDIAN_EVENT_KEEP, -
src/services/ap-timeline.js
r7ccdced re4119c2 351 351 } 352 352 353 /** 354 * Een pagina van de inbox, nieuwste eerst, met een cursor (shaer-ewrt). 355 * 356 * De inbox-lezing is vier poten van elk de nieuwste zestig, en daaronder hield 357 * het op: wat ouder was kon de app nergens vragen. Dit is de weg naar beneden. 358 * 359 * EEN LIMIET VOOR ALLE VIER SAMEN, om dezelfde reden als bij een gesprek: per 360 * poot afkappen geeft een pagina die bij de ene poot tot vorige week reikt en 361 * bij de andere tot gisteren, en dan valt er bij de volgende pagina iets in het 362 * gat ertussen. Over de unie afgekapt is een pagina compleet voor zijn periode. 363 * 364 * Alleen referenties: welke poot, welk object, welk stempel. Het kaartwerk doet 365 * de route met dezelfde bouwers als de verschil-lezing, zodat een kaart er 366 * hetzelfde uitziet hoe hij ook binnenkwam. 367 * 368 * De poten zijn dezelfde als in de volle lezing: de berichtpoot slaat over wat 369 * ook in de tijdlijn staat (getDirectMessages), en de stempels zijn die waarop 370 * elke poot daar sorteert. 371 */ 372 const INBOX_UNION = ` 373 SELECT 'timeline' AS leg, t.id AS ref, ${STEMPEL('COALESCE(t.published, t.created_at)')} AS stamp 374 FROM ap_timeline t WHERE t.slug = @slug 375 UNION ALL 376 SELECT 'message', m.object_uri, ${STEMPEL('COALESCE(m.published, m.created_at)')} 377 FROM ap_mentions m 378 WHERE m.slug = @slug 379 AND NOT EXISTS (SELECT 1 FROM ap_timeline t WHERE t.slug = m.slug AND t.id = m.object_uri) 380 UNION ALL 381 SELECT 'reply', i.object_uri, ${STEMPEL('COALESCE(i.published, i.created_at)')} 382 FROM ap_interactions i JOIN posts p ON p.id = i.post_id JOIN sites s ON s.id = p.site_id 383 WHERE s.slug = @slug AND i.kind = 'reply' 384 UNION ALL 385 SELECT 'sent', o.id, ${STEMPEL('o.created_at')} 386 FROM ap_outbox o WHERE o.site_slug = @slug`; 387 388 export function inboxPage(slug, { before = null, limit = 60 } = {}) { 389 try { 390 const n = Math.min(Math.max(parseInt(limit, 10) || 60, 1), 200); 391 const sep = String(before || '').indexOf('|'); 392 const bStamp = before && sep > 0 ? String(before).slice(0, sep) : null; 393 const bRef = before && sep > 0 ? String(before).slice(sep + 1) : null; 394 const rows = db.prepare(` 395 SELECT leg, ref, stamp FROM (${INBOX_UNION}) 396 WHERE (@bStamp IS NULL OR stamp < @bStamp OR (stamp = @bStamp AND ref < @bRef)) 397 ORDER BY stamp DESC, ref DESC LIMIT @n`).all({ slug, bStamp, bRef, n: n + 1 }); 398 const more = rows.length > n; 399 const page = more ? rows.slice(0, n) : rows; 400 return { rows: page, more, oldest: cursorOf(page[page.length - 1]) }; 401 } catch { return { rows: [], more: false, oldest: null }; } 402 } 403 353 404 // De kolommen die een bericht tot kaart maken. Een constante, want de 354 405 // gesprekslezing haalt dezelfde rows op: twee lijsten die uiteenlopen leveren
Note:
See TracChangeset
for help on using the changeset viewer.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)