Changeset 439f095 in Klonkt
- Timestamp:
- 07/28/2026 11:48:34 PM (6 weeks ago)
- Branches:
- main
- Children:
- 88d7c8f
- Parents:
- 0202104
- Location:
- src
- Files:
-
- 3 edited
-
routes/posts.js (modified) (2 diffs)
-
views/pages/connect.ejs (modified) (2 diffs)
-
views/pages/messages.ejs (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
src/routes/posts.js
r0202104 r439f095 939 939 ? Guardianship.offersCollection(`${gMe}/queues/offers`, site.slug, gMe).orderedItems 940 940 : []).filter((o) => o['shaer:ward'] === gMe && o['shaer:needsMyAccept']); 941 // FEP-633c §2: who guards this account (committed). Shown so a ward can always942 // see its guardians, not just pending offers. Derive a display @handle when the943 // 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 real950 // 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 }));963 941 renderPage(req, res, 'pages/messages', { 964 942 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, 966 944 success: req.query.success || null, error: req.query.error || null, 967 945 }); … … 1162 1140 const site = res.locals.site; 1163 1141 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 })); 1164 1162 renderPage(req, res, 'pages/connect', { 1165 1163 pageTitle: 'Connect', bodyClass: 'on-special', 1166 connections, 1164 connections, myGuardians, 1167 1165 success: req.query.success || null, error: req.query.error || null, 1168 1166 }); -
src/views/pages/connect.ejs
r0202104 r439f095 4 4 <% if (typeof success !== 'undefined' && success) { %><div class="alert alert-success"><%= success %></div><% } %> 5 5 <% 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">🛡 <%= 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 <% } %> 6 29 7 30 <form method="post" action="/news/follow" class="tl-follow"> … … 83 106 84 107 <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; } 85 125 .tl-wrap { max-width: 640px; margin: 1rem auto; padding: 0 1rem; } 86 126 .tl-title { margin: 0 0 .75rem; } -
src/views/pages/messages.ejs
r0202104 r439f095 22 22 </div> 23 23 <% }); %> 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;">🛡</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 its33 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>50 24 <% } %> 51 25 … … 145 119 146 120 <style> 147 /* Availability of the ward's guardians (FEP-633c 3.6): the buddy-list dot148 on the responsibility axis. Green available, yellow declared away, grey149 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; }157 121 .msg-wrap { max-width: 640px; margin: 1rem auto; padding: 0 1rem; } 158 122 .msg-title { margin: 0 0 .9rem; }
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)