Changeset 439f095 in Klonkt


Ignore:
Timestamp:
07/28/2026 11:48:34 PM (6 weeks ago)
Author:
Robin Genis <roboburr@…>
Branches:
main
Children:
88d7c8f
Parents:
0202104
Message:

De guardians-lijst verhuist van Berichten naar Connect

Connect is de pagina die vertelt met wie je verbonden bent, en een guardian is
precies de verbinding die een kind nooit zou moeten hoeven zoeken. In Berichten
stond hij boven een stroom meldingen, waar hij bij elk bezoek langsschuift; op
Connect staat hij bij de andere relaties, met een eigen kader omdat het geen
mensen zijn die je zelf gekozen hebt te volgen.

De stippen en de labels gaan mee zoals ze waren: beschikbaar, afwezig tot een
datum, offline. In Berichten blijft het guardianship-aanbod staan, want dat is
wel een bericht: er wordt iets aan je gevraagd.

Changed files:
src/routes/posts.js

  • myGuardians van /messages naar /connect, met de handle-helper mee; de helper in /messages was daarmee dood en is weg

src/views/pages/messages.ejs

  • het blok en zijn opmaak eruit

src/views/pages/connect.ejs

  • de guardians-sectie bovenaan, met eigen opmaak

remarks: in de browser nagelopen, ook op 375px: drie guardians in drie staten
op Connect, Berichten houdt alleen nog het aanbod. 276 tests groen.

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

Location:
src
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • src/routes/posts.js

    r0202104 r439f095  
    939939    ? Guardianship.offersCollection(`${gMe}/queues/offers`, site.slug, gMe).orderedItems
    940940    : []).filter((o) => o['shaer:ward'] === gMe && o['shaer:needsMyAccept']);
    941   // FEP-633c §2: who guards this account (committed). Shown so a ward can always
    942   // see its guardians, not just pending offers. Derive a display @handle when the
    943   // stored one is missing or is the escalation (inbox) handle rather than a @handle.
    944   const guardianHandle = (uri, cached) => {
    945     if (cached && cached.charAt(0) === '@') return cached;
    946     try { const u = new URL(uri); return `@${u.pathname.split('/').filter(Boolean).pop()}@${u.host}`; }
    947     catch { return uri; }
    948   };
    949   // With their availability (FEP-633c 3.6.1): the ward always sees the real
    950   // size of its safety net, not just the names. Owner-only by construction:
    951   // this page is the owner's.
    952   const gStatus = site ? Object.fromEntries(
    953     Guardianship.availability.statusesFor(site.slug, Guardianship.listGuardians(site.slug).map((g) => g.other_uri), Date.now())
    954       .map((s) => [s.id, s]),
    955   ) : {};
    956   const myGuardians = (site ? Guardianship.listGuardians(site.slug) : [])
    957     .map((g) => ({
    958       uri: g.other_uri,
    959       handle: guardianHandle(g.other_uri, g.other_handle),
    960       availability: (gStatus[g.other_uri] || {})['shaer:availability'] || 'active',
    961       awayUntil: (gStatus[g.other_uri] || {})['shaer:awayUntil'] || null,
    962     }));
    963941  renderPage(req, res, 'pages/messages', {
    964942    pageTitleKey: 'msg.title', bodyClass: 'on-special', items, seenAt,
    965     hasMore, nextOffset: offset + FEED_PAGE, moreBase, guardianOffers, myGuardians,
     943    hasMore, nextOffset: offset + FEED_PAGE, moreBase, guardianOffers,
    966944    success: req.query.success || null, error: req.query.error || null,
    967945  });
     
    11621140  const site = res.locals.site;
    11631141  const connections = site ? ActivityPubService.listConnections(site.slug) : [];
     1142  // FEP-633c §2: the ward always sees who guards it, and §3.6 how available
     1143  // each of them is. Connect is where "who am I connected to" belongs; a
     1144  // guardian is the one connection a ward should never have to hunt for.
     1145  // Owner-only by construction: this page is the owner's.
     1146  const guardianHandle = (uri, cached) => {
     1147    if (cached && cached.charAt(0) === '@') return cached;
     1148    try { const u = new URL(uri); return `@${u.pathname.split('/').filter(Boolean).pop()}@${u.host}`; }
     1149    catch { return uri; }
     1150  };
     1151  const gStatus = site ? Object.fromEntries(
     1152    Guardianship.availability.statusesFor(site.slug, Guardianship.listGuardians(site.slug).map((g) => g.other_uri), Date.now())
     1153      .map((s) => [s.id, s]),
     1154  ) : {};
     1155  const myGuardians = (site ? Guardianship.listGuardians(site.slug) : [])
     1156    .map((g) => ({
     1157      uri: g.other_uri,
     1158      handle: guardianHandle(g.other_uri, g.other_handle),
     1159      availability: (gStatus[g.other_uri] || {})['shaer:availability'] || 'active',
     1160      awayUntil: (gStatus[g.other_uri] || {})['shaer:awayUntil'] || null,
     1161    }));
    11641162  renderPage(req, res, 'pages/connect', {
    11651163    pageTitle: 'Connect', bodyClass: 'on-special',
    1166     connections,
     1164    connections, myGuardians,
    11671165    success: req.query.success || null, error: req.query.error || null,
    11681166  });
  • src/views/pages/connect.ejs

    r0202104 r439f095  
    44  <% if (typeof success !== 'undefined' && success) { %><div class="alert alert-success"><%= success %></div><% } %>
    55  <% if (typeof error !== 'undefined' && error) { %><div class="alert alert-error"><%= error %></div><% } %>
     6
     7  <%# FEP-633c §2 + §3.6: who guards this account, and how available each of
     8      them is. Connect is the "who am I connected to" page, and a guardian is
     9      the one connection a ward should never have to hunt for. Availability in
     10      buddy-list language on the responsibility axis; the labels are UI, the
     11      wire keeps the spec terms. Never public: this page is the owner's. %>
     12  <% if (typeof myGuardians !== 'undefined' && myGuardians.length) { %>
     13    <section class="conn-guardians">
     14      <h2 class="conn-gtitle">&#128737; <%= t('msg.guardians_label') %></h2>
     15      <ul class="conn-glist">
     16        <% myGuardians.forEach(function(g){
     17             var _st = g.availability === 'away' ? 'away' : (g.availability === 'dormant' ? 'dormant' : 'active');
     18             var _lbl = _st === 'away' ? t('msg.g_away', { date: g.awayUntil ? formatDate(g.awayUntil) : '?' })
     19                      : (_st === 'dormant' ? t('msg.g_offline') : t('msg.g_available')); %>
     20          <li class="conn-guardian">
     21            <span class="conn-gdot conn-gdot--<%= _st %>" aria-hidden="true"></span>
     22            <span class="conn-ghandle"><%= g.handle %></span>
     23            <span class="conn-gstate"><%= _lbl %></span>
     24          </li>
     25        <% }); %>
     26      </ul>
     27    </section>
     28  <% } %>
    629
    730  <form method="post" action="/news/follow" class="tl-follow">
     
    83106
    84107<style>
     108  /* Guardians (FEP-633c 3.6): the buddy-list dot on the responsibility axis.
     109     Green available, yellow declared away with an end, grey observed dormant
     110     (one answer restores). Set apart from the follow list on purpose: these
     111     are not people you chose to follow, they are the ones looking after you. */
     112  .conn-guardians { margin: 0 0 1.25rem; padding: .8rem 1rem; border-radius: 12px;
     113    border: 1px solid color-mix(in srgb, var(--ink, #000) 10%, transparent);
     114    background: color-mix(in srgb, var(--ink, #000) 2.5%, transparent);
     115    border-left: 3px solid #35a2ff; }
     116  .conn-gtitle { margin: 0 0 .5rem; font-size: .95rem; }
     117  .conn-glist { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: .35rem; }
     118  .conn-guardian { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; }
     119  .conn-gdot { width: 8px; height: 8px; border-radius: 50%; flex: 0 0 auto; }
     120  .conn-gdot--active { background: #4caf7d; }
     121  .conn-gdot--away { background: #e0a83a; }
     122  .conn-gdot--dormant { background: #8a93a3; }
     123  .conn-ghandle { font-weight: 600; }
     124  .conn-gstate { color: var(--ink-soft, #888); font-size: .82rem; }
    85125  .tl-wrap { max-width: 640px; margin: 1rem auto; padding: 0 1rem; }
    86126  .tl-title { margin: 0 0 .75rem; }
  • src/views/pages/messages.ejs

    r0202104 r439f095  
    2222    </div>
    2323    <% }); %>
    24   <% } %>
    25 
    26   <%# FEP-633c §2: the ward always sees who guards it (committed relations). %>
    27   <% if (typeof myGuardians !== 'undefined' && myGuardians.length) { %>
    28     <div class="alert" style="display:flex;align-items:center;gap:12px;flex-wrap:wrap;border-left:3px solid #35a2ff;">
    29       <span style="font-size:1.4em;">&#128737;</span>
    30       <div style="flex:1;min-width:200px;">
    31         <strong><%= t('msg.guardians_label') %></strong><br>
    32         <%# Availability (FEP-633c 3.6): the ward sees the real size of its
    33             safety net, in buddy-list language on the responsibility axis.
    34             Labels are UI (Robins besluit); the wire keeps the spec terms.
    35             Owner-only by construction: this page is the owner's. %>
    36         <span class="msg-guardians">
    37           <% myGuardians.forEach(function(g){
    38                var _st = g.availability === 'away' ? 'away' : (g.availability === 'dormant' ? 'dormant' : 'active');
    39                var _lbl = _st === 'away' ? t('msg.g_away', { date: g.awayUntil ? formatDate(g.awayUntil) : '?' })
    40                         : (_st === 'dormant' ? t('msg.g_offline') : t('msg.g_available')); %>
    41             <span class="msg-guardian">
    42               <span class="msg-gdot msg-gdot-<%= _st %>" aria-hidden="true"></span>
    43               <%= g.handle %>
    44               <span class="msg-gstate"><%= _lbl %></span>
    45             </span>
    46           <% }); %>
    47         </span>
    48       </div>
    49     </div>
    5024  <% } %>
    5125
     
    145119
    146120<style>
    147   /* Availability of the ward's guardians (FEP-633c 3.6): the buddy-list dot
    148      on the responsibility axis. Green available, yellow declared away, grey
    149      observed dormant (one answer restores). */
    150   .msg-guardians { display: flex; flex-wrap: wrap; gap: .35rem 1rem; }
    151   .msg-guardian { display: inline-flex; align-items: center; gap: .35rem; white-space: nowrap; }
    152   .msg-gdot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; }
    153   .msg-gdot-active { background: #4caf7d; }
    154   .msg-gdot-away { background: #e0a83a; }
    155   .msg-gdot-dormant { background: #8a93a3; }
    156   .msg-gstate { color: var(--ink-soft, #888); font-size: .82rem; }
    157121  .msg-wrap { max-width: 640px; margin: 1rem auto; padding: 0 1rem; }
    158122  .msg-title { margin: 0 0 .9rem; }
Note: See TracChangeset for help on using the changeset viewer.