source: Klonkt/src/services/conversation-key.js@ 6e9bc70

main
Last change on this file since 6e9bc70 was 6e9bc70, checked in by Bart <bart@…>, 2 days ago

Zachte groepen: een gesprek is de set deelnemers, niet één tegenpartij

Een direct bericht aan Anna en Ben bestond al als groep -- to_actors droeg de
hele set -- maar elke lezing sleutelde op één other. Een uitgaand bericht
aan twee mensen viel in twee gesprekken uiteen, en een binnenkomend bericht
van Anna aan jou en Ben kwam alleen bij Anna terecht: Ben viel eruit, want de
adressering van een binnenkomer werd nergens bewaard.

Nu is er een sleutel, één keer berekend bij het schrijven
(conversation-key.js): voor één ander zijn kale URI, precies zoals het altijd
was, dus bestaande gesprekken, leesmarkeringen en ?with=-URL's blijven geldig
zonder migratie; voor een groep de gesorteerde set, gescheiden door een
spatie. Koppen, geschiedenis, leesmarkeringen en ongelezen lezen hem allemaal
via dezelfde unie, en werken daarmee per gesprek zonder dat er in die queries
iets hoefde te veranderen. Oude rijen hebben geen sleutel en lezen zoals ze
altijd lazen.

Alleen directe berichten groeperen: een openbare post aan jou en Ben is geen
besloten gesprek met z'n drieën.

/messages neemt meer dan één with, en elk item draagt shaer:conversation en
shaer:participants. Uitgaande directe notes krijgen een FEP-7888 context --
die van de ouder als het een antwoord is, anders een nieuwe -- die bewaard en
uitgestuurd wordt, maar geen bubbels samenvoegt: een bubbel die Carla toont
terwijl je antwoord haar niet bereikt, zou liegen over wie meeleest.

shaer-mbji
Co-Authored-By: Claude Opus 5.5 <claude@…>

  • Property mode set to 100644
File size: 3.6 KB
Line 
1/**
2 * Zachte groepen (shaer-mbji): een gesprek is de SET deelnemers, niet één
3 * tegenpartij.
4 *
5 * Een direct bericht aan Anna en Ben bestond al als groep op de draad --
6 * ap_outbox.to_actors droeg de hele set -- maar elke lezing hier sleutelde op
7 * één `other`. Een uitgaand bericht aan twee mensen viel daardoor in twee
8 * gesprekken uiteen, en een binnenkomend bericht van Anna aan jou en Ben kwam
9 * alleen bij Anna terecht: Ben viel eruit. Een gesprek met drie mensen bestond
10 * zo alleen als losse één-op-één-bubbels die elk een deel ervan droegen.
11 *
12 * DE SLEUTEL:
13 *
14 * één ander -> zijn actor-URI, kaal. Precies wat het altijd was, dus elk
15 * bestaand gesprek, elke leesmarkering en elke ?with=-URL
16 * blijft geldig. Niets te migreren.
17 * meer -> de URI's gesorteerd, gescheiden door één spatie. Een URI kan
18 * geen ongecodeerde spatie bevatten, dus de scheiding is
19 * ondubbelzinnig, en sorteren maakt {Anna, Ben} en {Ben, Anna}
20 * hetzelfde gesprek.
21 *
22 * Ikzelf zit nooit in de sleutel: ik ben in elk gesprek, dus mij noemen zegt
23 * niets en zou dezelfde groep twee sleutels geven, afhankelijk van wie hem
24 * begon.
25 *
26 * ZACHT BETEKENT: WIE GEADRESSEERD IS, ZIT ERIN. Geen aanmaakstap, geen
27 * eigenaar. Komt Carla er halverwege bij, dan is {Anna, Ben, Carla} een ander
28 * gesprek dan {Anna, Ben} -- en dat klopt, want een antwoord daar bereikt
29 * Carla en een antwoord in de oude bubbel niet. `context` (FEP-7888) reist mee
30 * en wordt bewaard, maar voegt hier geen bubbels samen: een bubbel die Carla
31 * toont terwijl je antwoord haar niet bereikt, zou liegen over wie meeleest.
32 *
33 * Puur, zonder afhankelijkheden: de inbox, de bezorging en de lezing gebruiken
34 * allemaal dezelfde sleutel, en dat kan alleen als hij op één plek staat.
35 */
36
37const PUBLIC = new Set([
38 'https://www.w3.org/ns/activitystreams#Public',
39 'as:Public',
40 'Public',
41]);
42
43/** Is dit een persoon, en niet een publiek of een volgerslijst? */
44export function isPersonalAddress(uri) {
45 if (typeof uri !== 'string' || !/^https?:\/\//i.test(uri)) return false;
46 if (PUBLIC.has(uri)) return false;
47 return !/\/followers\/?$/.test(uri);
48}
49
50const lijst = (v) => (Array.isArray(v) ? v : (v ? [v] : []))
51 .map((x) => (typeof x === 'string' ? x : (x && typeof x === 'object' ? x.id : null)))
52 .filter((x) => typeof x === 'string');
53
54/**
55 * De andere deelnemers van een binnenkomende note: de afzender plus iedere
56 * persoon in to/cc, zonder mij. Gesorteerd en ontdubbeld.
57 */
58export function participantsOf(note, author, me) {
59 const set = new Set();
60 if (isPersonalAddress(author)) set.add(author);
61 for (const u of [...lijst(note && note.to), ...lijst(note && note.cc)]) {
62 if (isPersonalAddress(u)) set.add(u);
63 }
64 set.delete(me);
65 return [...set].sort();
66}
67
68/** De gesprekssleutel van een set andere deelnemers. */
69export function conversationKey(others) {
70 const set = [...new Set((others || []).filter(isPersonalAddress))].sort();
71 return set.join(' ');
72}
73
74/** De deelnemers achter een sleutel. Een kale URI is een gesprek met één. */
75export function participantsOfKey(key) {
76 return String(key || '').split(' ').filter(isPersonalAddress);
77}
78
79/** Een groep: meer dan één ander. */
80export function isGroupKey(key) {
81 return participantsOfKey(key).length > 1;
82}
83
84/** De context van een note (FEP-7888): een string of een object met een id. */
85export function contextOf(note) {
86 const c = note && note.context;
87 const id = typeof c === 'string' ? c : (c && typeof c === 'object' ? c.id : null);
88 return typeof id === 'string' && /^https?:\/\//i.test(id) ? id : null;
89}
Note: See TracBrowser for help on using the repository browser.