Changeset 7875a80 in Klonkt for src/services/ActivityPubService.js
- Timestamp:
- 08/06/2026 01:56:08 AM (5 weeks ago)
- Branches:
- main
- Children:
- 0448e34
- Parents:
- 04aca12
- git-author:
- Robin <roboburr@…> (08/06/2026 01:55:53 AM)
- git-committer:
- Claude (agent) <aiclaude@…> (08/06/2026 01:56:08 AM)
- File:
-
- 1 edited
-
src/services/ActivityPubService.js (modified) (3 diffs)
Legend:
- Unmodified
- Added
- Removed
-
src/services/ActivityPubService.js
r04aca12 r7875a80 639 639 items.push({ 640 640 type: 'sent', outboxId: m.id, to_handle: m.to_handle, in_reply_to: m.in_reply_to, 641 content: m.content, editable: m.editable, language: m.language, created_at: m.created_at, 641 post_slug: m.post_slug, content: m.content, editable: m.editable, 642 language: m.language, created_at: m.created_at, 642 643 }); 643 644 } 644 645 } catch { /* ignore */ } 646 // Een verzonden antwoord kent zijn post_slug maar niet de titel (ap_outbox 647 // bewaart die niet). Zonder titel toont een draad waarin JIJ als enige iets 648 // zei alleen een slug, dus vullen we ze in één query aan. 649 try { 650 const missing = [...new Set(items.filter((i) => i.post_slug && !i.post_title).map((i) => i.post_slug))]; 651 if (missing.length) { 652 const rows = db.prepare( 653 `SELECT slug, title FROM posts WHERE slug IN (${missing.map(() => '?').join(',')}) 654 AND site_id = (SELECT id FROM sites WHERE slug = ?)`, 655 ).all(...missing, slug); 656 const byslug = new Map(rows.map((r) => [r.slug, r.title])); 657 for (const i of items) if (i.post_slug && !i.post_title) i.post_title = byslug.get(i.post_slug) || null; 658 } 659 } catch { /* zonder titel valt de draad terug op de slug */ } 645 660 items.sort((a, b) => _msgTs(b) - _msgTs(a)); // NaN-safe (zie getNotifications) 646 661 const out = []; … … 656 671 out.push(it); 657 672 } 658 return out.slice(off, off + lim); 673 // Antwoorden, mentions en je eigen verzonden berichten vouwen samen tot 674 // draden; likes/boosts/follows/reports blijven losse regels. Na deze stap 675 // telt een draad als één item voor de paginering, wat klopt: je scrolt door 676 // gesprekken, niet door losse zinnen. 677 return groupConversations(out).slice(off, off + lim); 678 } 679 680 // De drie soorten die samen een gesprek vormen. Vroeger zaten ze in drie 681 // aparte chips: 'reply' en 'mention' onder Berichten/Gesprekken (afhankelijk van 682 // de zichtbaarheid) en 'sent' onder Verzonden. Wie een uitwisseling wilde volgen 683 // moest dus tussen chips heen en weer, terwijl het één draad is. 684 const CONV_TYPES = new Set(['reply', 'mention', 'sent']); 685 686 /** Waar hangt dit bericht aan? Twee soorten draden, en de volgorde telt: 687 * 688 * 1. Aan een post van jou. Een ontvangen antwoord kent zijn post via de join 689 * op `posts`, een verzonden antwoord via ap_outbox.post_slug. Dat is 690 * dezelfde sleutel, en daarom staan ze nu in dezelfde draad. 691 * 2. Aan een persoon. Een mention hangt aan niets van jou (het is iemands 692 * eigen post waarin je genoemd wordt) en heeft geen post_slug; die draad 693 * loopt per tegenpartij. 694 * 695 * De post wint van de persoon: twee mensen die onder dezelfde post reageren 696 * voeren één gesprek, geen twee. Geeft null terug voor alles wat geen gesprek 697 * is (likes, boosts, follows, reports, poll-uitslagen); die stromen ongemoeid 698 * door. 699 */ 700 export function threadKey(it) { 701 if (!it || !CONV_TYPES.has(it.type)) return null; 702 if (it.post_slug) return `post:${it.post_slug}`; 703 const who = it.handle || it.to_handle || ''; 704 const norm = String(who).trim().toLowerCase().replace(/^@/, ''); 705 return norm ? `actor:${norm}` : null; 706 } 707 708 /** Vouw losse berichten samen tot draden, met alles wat geen gesprek is 709 * ongemoeid ertussen. Verwacht [items] al gesorteerd op created_at aflopend 710 * (zoals getMessages ze aanlevert); de draad komt daardoor op de plek van zijn 711 * nieuwste bericht te staan en `created_at` van de draad IS dat bericht. Binnen 712 * de draad draait het om: een gesprek leest naar beneden, oud naar nieuw. 713 */ 714 export function groupConversations(items) { 715 const threads = new Map(); 716 const out = []; 717 for (const it of items || []) { 718 const key = threadKey(it); 719 if (!key) { out.push(it); continue; } 720 let t = threads.get(key); 721 if (!t) { 722 // Eerste keer dat we deze draad zien = het nieuwste bericht erin, want de 723 // invoer is aflopend gesorteerd. Vandaar created_at hier en niet later. 724 t = { type: 'thread', key, post: null, people: [], messages: [], created_at: it.created_at }; 725 threads.set(key, t); 726 out.push(t); 727 } 728 t.messages.push(it); 729 // De context bij de draad: gaat het over een post, dan hoort de link 730 // erbij, anders is een los antwoord in een lijst niet te plaatsen. 731 // De titel blijft LEEG zolang hij onbekend is, in plaats van terug te 732 // vallen op de slug: het nieuwste bericht in een draad is vaak je eigen 733 // verzonden antwoord, en dat kent alleen de slug. Zou die de titel worden, 734 // dan kan het ontvangen antwoord eronder de echte titel niet meer 735 // invullen. De terugval op de slug hoort in de weergave, niet in de data. 736 if (it.post_slug) { 737 if (!t.post) t.post = { slug: it.post_slug, title: it.post_title || null }; 738 else if (!t.post.title && it.post_title) t.post.title = it.post_title; 739 } 740 } 741 for (const t of threads.values()) { 742 t.messages.sort((a, b) => _msgTs(a) - _msgTs(b)); 743 t.count = t.messages.length; 744 // Wie zit er in dit gesprek, jij niet meegerekend: 'sent' ben jij. 745 const seen = new Set(); 746 for (const m of t.messages) { 747 if (m.type === 'sent') continue; 748 const h = m.handle || m.name; 749 if (!h || seen.has(h)) continue; 750 seen.add(h); 751 t.people.push({ name: m.name, handle: m.handle, icon: m.icon, url: m.url }); 752 } 753 // Heb JIJ in deze draad iets gezegd? Bepaalt of hij als uitwisseling of als 754 // onbeantwoord bericht leest. 755 t.mine = t.messages.some((m) => m.type === 'sent'); 756 } 757 return out; 659 758 } 660 759 … … 2907 3006 } 2908 3007 export function listOutbox(siteSlug) { 2909 return db.prepare('SELECT id, content, to_handle, in_reply_to, language, created_at FROM ap_outbox WHERE site_slug = ? ORDER BY created_at DESC') 3008 // post_slug reist mee sinds Berichten gesprekken toont: het is de sleutel 3009 // waarop een verzonden antwoord bij de ontvangen antwoorden op dezelfde post 3010 // gaat staan (zie threadKey). Zonder die kolom viel een uitwisseling uit 3011 // elkaar in "Verzonden" en "Gesprekken". 3012 return db.prepare('SELECT id, content, to_handle, to_actor, post_slug, in_reply_to, language, created_at FROM ap_outbox WHERE site_slug = ? ORDER BY created_at DESC') 2910 3013 .all(siteSlug).map((r) => { const c = stripLeadingMentions(r.content); return { ...r, content: c, editable: outboxEditableText(c) }; }); 2911 3014 }
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)