Changeset c010b42 in Klonkt for src/routes/posts.js


Ignore:
Timestamp:
08/06/2026 08:46:01 AM (5 weeks ago)
Author:
Claude (agent) <aiclaude@…>
Branches:
main
Children:
0e27e54
Parents:
02fb7ce
git-author:
Robin <roboburr@…> (08/06/2026 08:46:00 AM)
git-committer:
Claude (agent) <aiclaude@…> (08/06/2026 08:46:01 AM)
Message:

Eén schrijfpad voor reacties: setReaction (shaer-9e9 fase 1, stap 2)

Alle plekken die "ik heb hierop gereageerd" opschreven gaan nu door
setReaction(slug, uri, kind, on, opts): de tussentabel ap_my_reactions is de
waarheid, de vlaggen op ap_timeline zijn de afgeleide. Dat zijn de twee takken
van de C2S-outbox (Like/Announce en Undo) en de vier webhandlers (interact-like,
interact-boost, tijdlijn-like, tijdlijn-boost).

Twee dingen zitten er bewust in.

opts.note, omdat een boost geen vlag zet maar de post je tijdlijn IN trekt als je
de auteur niet volgt -- anders heeft de vlag geen rij om op te landen en
verschijnt de boost nergens, ook niet in de Cirkel. Dat is de valkuil die in het
plan met naam staat.

opts.flagUri, omdat de twee bronnen vandaag verschillend gesleuteld worden: de
tussentabel op de URI die de client stuurde, de vlag op de opgeloste object-URI.
Meestal gelijk, niet gegarandeerd. Die naad houdt deze stap gedragsbehoudend;
het samentrekken van de sleutels is fase 2, met datamigratie.

CORRECTIE OP HET PLAN: ik schreef dat deze stap "per definitie" geen
gedragswijziging geeft omdat lezen niet verandert. Dat klopt niet. Nu de
interact-route ook de kolom vult, verschijnt een like die eerder alleen in de
tussentabel stond voortaan ook in de Krant. Dat is de bedoelde convergentie,
maar het is wel zichtbaar -- en het hoort in de notitie te staan in plaats van
weggeredeneerd.

CORRECTIE OP DE TESTS: de karakteriseringstests zouden volgens hun eigen kop
gaan falen na deze stap. Dat gebeurde niet, en terecht: ze roepen de primitieven
aan en die zijn niet veranderd. De kop is bijgewerkt en er zijn vier tests bij
die wel bewaken wat deze stap oplevert -- dat setReaction beide bronnen vult,
dat een boost met note de rij aanmaakt en in de Cirkel belandt, dat de
gescheiden sleutels blijven werken, en dat rommelige invoer niets doet in plaats
van iets halfs.

Suite 426/426.

Co-Authored-By: Claude Opus 5 <noreply@…>

File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/routes/posts.js

    r02fb7ce rc010b42  
    843843      .then((note) => note && ActivityPubService.sendInteraction(site, on ? 'like' : 'unlike', note.object_uri || uri, note.actor_uri))
    844844      .catch((e) => console.warn('[AP] remote like failed:', e.message));
    845     ActivityPubService.setMyReaction(site.slug, uri, 'like', on);
     845    // Eén schrijfpad (shaer-9e9): tussentabel + afgeleide vlag.
     846    ActivityPubService.setReaction(site.slug, uri, 'like', on);
    846847  }
    847848  if (req.get('X-Requested-With') === 'fetch') return res.json({ ok: true, on });
     
    862863        const id = note.object_uri || uri;
    863864        return Promise.resolve(ActivityPubService.sendInteraction(site, on ? 'boost' : 'unboost', id, note.actor_uri))
    864           // Boost → store the post in the timeline (even if you don't follow the author) so it
    865           // surfaces in the Cirkel; unboost → just clear the flag.
    866           .then(() => on ? ActivityPubService.upsertBoostedNote(site.slug, note) : ActivityPubService.unmarkBoosted(site.slug, id));
     865          // De note gaat mee: een boost zet niet alleen een vlag maar trekt de
     866          // post je tijdlijn in, ook als je de auteur niet volgt, zodat hij in
     867          // de Cirkel verschijnt.
     868          .then(() => ActivityPubService.setReaction(site.slug, uri, 'boost', on, { flagUri: id, note: on ? note : null }));
    867869      })
    868870      .catch((e) => console.warn('[AP] remote boost failed:', e.message));
     871    // De tussentabel meteen, zodat de knop klopt voordat de resolve terug is.
    869872    ActivityPubService.setMyReaction(site.slug, uri, 'boost', on);
    870873  }
     
    12791282    on = !ActivityPubService.getTimelineReaction(site.slug, note).liked;
    12801283    try { await ActivityPubService.sendInteraction(site, on ? 'like' : 'unlike', note, (req.body.author || '').toString()); } catch (e) { /* ignore */ }
    1281     if (on) ActivityPubService.markLiked(site.slug, note); else ActivityPubService.unmarkLiked(site.slug, note);
     1284    ActivityPubService.setReaction(site.slug, note, 'like', on);
    12821285  }
    12831286  if (req.get('X-Requested-With') === 'fetch') return res.json({ ok: true, on });
     
    12931296    on = !ActivityPubService.getTimelineReaction(site.slug, note).boosted;
    12941297    try { await ActivityPubService.sendInteraction(site, on ? 'boost' : 'unboost', note, (req.body.author || '').toString()); } catch (e) { /* ignore */ }
     1298    ActivityPubService.setReaction(site.slug, note, 'boost', on); // instant UI state
    12951299    if (on) {
    1296       ActivityPubService.markBoosted(site.slug, note); // instant UI state
    12971300      // Fire-and-forget: re-resolve the note so the cached row is refreshed
    12981301      // (cover/content) — boosting again heals a stale copy from EVERY boost
    12991302      // path, not just the interact page.
    13001303      ActivityPubService.resolveRemoteNote(note)
    1301         .then((n) => { if (n) ActivityPubService.upsertBoostedNote(site.slug, n); })
     1304        .then((n) => { if (n) ActivityPubService.setReaction(site.slug, note, 'boost', true, { note: n }); })
    13021305        .catch(() => { /* best-effort */ });
    1303     } else {
    1304       ActivityPubService.unmarkBoosted(site.slug, note);
    13051306    }
    13061307  }
Note: See TracChangeset for help on using the changeset viewer.