source: Klonkt/src/views/pages/connect.ejs@ 822d16d

main
Last change on this file since 822d16d was 822d16d, checked in by Robin <roboburr@…>, 4 weeks ago

De volglijst-knoppen stonden op een pagina waar niets naartoe linkt

Robin kwam er bij de eerste echte verhuizing op uit: hij kon /following alleen
handmatig bereiken, niet via de UI.

Klopt. De tabbalk heeft vier tabbladen (news, connect, messages, blocking) en
/following staat daar niet bij. In fedi-tabs.ejs staat het met zoveel woorden:
"legacy values 'following'/'followers' -> Connect". Die pagina werkt nog als je
de URL intypt, maar er linkt niets meer naartoe, en ik had mijn download- en
importblok daar neergezet. Dus wel gebouwd, wel getest, en toch onbereikbaar.

Blok verplaatst naar connect.ejs, direct onder het volgformulier, want dat is de
pagina die de Connect-tab werkelijk opent.

Changed files:
src/views/pages/connect.ejs

  • het uitklapblok "Je volglijst meenemen" met de downloadknop en het plakveld

remarks: /following laat het blok ook nog staan; die pagina schaadt niet en wie
er een bladwijzer op heeft houdt zijn knop. 926 groen. Gerenderd gecontroleerd:
beide formulieren aanwezig, geen onvertaalde sleutels.

Dit was een fout van het soort dat een suite niet vangt: alle tests groen, de
route bestaat, de knop rendert, en toch kan niemand erbij. Alleen iemand die het
echt probeert vindt dit.

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

  • Property mode set to 100644
File size: 11.1 KB
Line 
1<div class="tl-wrap">
2 <%- include('../partials/fedi-tabs', { active: 'connect' }) %>
3 <h1 class="tl-title"><%= t('tl.connect') %></h1>
4 <% if (typeof success !== 'undefined' && success) { %><div class="alert alert-success"><%= success %></div><% } %>
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">&#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 <% } %>
29
30 <form method="post" action="/news/follow" class="tl-follow">
31 <input type="text" name="handle" placeholder="@naam@server.social of site.com" autocomplete="off" spellcheck="false" required>
32 <button type="submit" class="btn btn-primary"><%= t('tl.follow_btn') %></button>
33 <label class="tl-follow-ab" title="<%= t('tl.autoboost_hint') %>"><input type="checkbox" name="auto_boost" value="1"> &#10024; <%= t('tl.autoboost_follow') %></label>
34 </form>
35
36 <%# Je volglijst meenemen. Hoort HIER en niet op /following: de tabbalk stuurt
37 'following' door naar Connect en er linkt niets meer naar die oude pagina.
38 Wie verhuist moet dit kunnen vinden zonder een URL uit zijn hoofd te typen. %>
39 <details class="tl-move">
40 <summary><%= t('tl.move_title') %></summary>
41 <p class="tl-move-hint"><%= t('tl.move_hint') %></p>
42 <p><a class="btn" href="/news/following.csv" download>&#11015; <%= t('tl.move_export') %></a></p>
43 <form method="post" action="/news/following/import">
44 <label class="tl-move-lbl" for="tl-move-csv"><%= t('tl.move_import_lbl') %></label>
45 <textarea id="tl-move-csv" name="csv" rows="5" spellcheck="false"
46 placeholder="Account address,Show boosts,Notify on new posts,Languages,Featured"></textarea>
47 <button type="submit" class="btn btn-primary"><%= t('tl.move_import') %></button>
48 </form>
49 </details>
50
51 <%
52 var conns = connections || [];
53 var reachable = conns.filter(function (c) { return !c.unreachable; });
54 var unreachable = conns.filter(function (c) { return c.unreachable; });
55 // Display fields for one connection: name/handle (fallback: derive from actor_uri).
56 function connView(c) {
57 var host = '', user = '';
58 try { var u = new URL(c.actor_uri); host = u.hostname; user = (u.pathname.split('/').filter(Boolean).pop() || ''); } catch (e) {}
59 var handle = c.handle || (user ? ('@' + user + '@' + host) : c.actor_uri);
60 var display = c.name || handle;
61 return { handle: handle, display: display,
62 initial: String(display || '?').replace(/^@/, '').charAt(0).toUpperCase(),
63 profileUrl: c.url || c.actor_uri };
64 }
65 function arrow(dir) { return dir === 'mutual' ? '↔' : (dir === 'following' ? '→' : '←'); }
66 %>
67
68 <% if (reachable.length) { %>
69 <ul class="tl-foll-list">
70 <% reachable.forEach(function (c) { var v = connView(c); %>
71 <li class="tl-foll">
72 <span class="tl-foll-av"><% if (c.icon) { %><img src="<%= avatar(c.icon, 96) %>" alt=""><% } else { %><%= v.initial %><% } %></span>
73 <span class="tl-foll-meta">
74 <a href="<%= v.profileUrl %>" target="_blank" rel="nofollow noopener"><%= v.display %></a>
75 <span class="conn-sub">
76 <span class="conn-dir conn-dir--<%= c.direction %>"><%= arrow(c.direction) %> <%= t('tl.dir_' + c.direction) %></span>
77 <% if (c.follower && c.last_delivery_at) { %><span class="conn-deliv">&middot; <%= t('tl.last_delivery') %>: <%= formatDateTime(c.last_delivery_at) %></span><% } %>
78 </span>
79 </span>
80 <% if (c.following) { %>
81 <form method="post" action="/news/autoboost" class="tl-foll-ab" title="<%= t('tl.autoboost_hint') %>">
82 <input type="hidden" name="actor_uri" value="<%= c.actor_uri %>">
83 <label><input type="checkbox" name="auto_boost" value="1" <%= c.auto_boost ? 'checked' : '' %> data-autosubmit> &#10024; <%= t('tl.autoboost') %></label>
84 </form>
85 <form method="post" action="/news/unfollow"><input type="hidden" name="actor_uri" value="<%= c.actor_uri %>"><button type="submit" class="tl-unfollow"><%= t('tl.unfollow') %></button></form>
86 <% } %>
87 </li>
88 <% }); %>
89 </ul>
90 <% } else { %>
91 <p class="tl-empty"><%= t('tl.connect_empty') %></p>
92 <% } %>
93
94 <% if (unreachable.length) { %>
95 <details class="conn-unreach">
96 <summary>&#9888; <%= t('tl.unreachable') %> (<%= unreachable.length %>)</summary>
97 <p class="conn-unreach-lead"><%= t('tl.unreachable_lead') %></p>
98 <ul class="tl-foll-list">
99 <% unreachable.forEach(function (c) { var v = connView(c); %>
100 <li class="tl-foll is-stale">
101 <span class="tl-foll-av"><% if (c.icon) { %><img src="<%= avatar(c.icon, 96) %>" alt=""><% } else { %><%= v.initial %><% } %></span>
102 <span class="tl-foll-meta">
103 <a href="<%= v.profileUrl %>" target="_blank" rel="nofollow noopener"><%= v.display %></a>
104 <span class="conn-sub">
105 <span class="conn-dir conn-dir--<%= c.direction %>"><%= arrow(c.direction) %> <%= t('tl.dir_' + c.direction) %></span>
106 <% if (c.last_delivery_at) { %><span class="conn-deliv">&middot; <%= t('tl.last_delivery') %>: <%= formatDateTime(c.last_delivery_at) %></span><% } else { %><span class="conn-never">&middot; <%= t('tl.never_delivered') %></span><% } %>
107 <% if (c.last_error_at) { %><span class="conn-err">&middot; <%= t('tl.delivery_failed') %>: <%= formatDateTime(c.last_error_at) %></span><% } %>
108 </span>
109 </span>
110 <% if (c.follower_id) { %>
111 <form method="post" action="/followers/<%= c.follower_id %>/remove" onsubmit="return confirm('<%= t('tl.remove_confirm') %>')">
112 <button type="submit" class="tl-unfollow"><%= t('tl.remove_follower') %></button>
113 </form>
114 <% } %>
115 </li>
116 <% }); %>
117 </ul>
118 </details>
119 <% } %>
120</div>
121
122<style>
123 /* Guardians (FEP-633c 3.6): the buddy-list dot on the responsibility axis.
124 Green available, yellow declared away with an end, grey observed dormant
125 (one answer restores). Set apart from the follow list on purpose: these
126 are not people you chose to follow, they are the ones looking after you. */
127 .conn-guardians { margin: 0 0 1.25rem; padding: .8rem 1rem; border-radius: 12px;
128 border: 1px solid color-mix(in srgb, var(--ink, #000) 10%, transparent);
129 background: color-mix(in srgb, var(--ink, #000) 2.5%, transparent);
130 border-left: 3px solid #35a2ff; }
131 .conn-gtitle { margin: 0 0 .5rem; font-size: .95rem; }
132 .conn-glist { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: .35rem; }
133 .conn-guardian { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; }
134 .conn-gdot { width: 8px; height: 8px; border-radius: 50%; flex: 0 0 auto; }
135 .conn-gdot--active { background: #4caf7d; }
136 .conn-gdot--away { background: #e0a83a; }
137 .conn-gdot--dormant { background: #8a93a3; }
138 .conn-ghandle { font-weight: 600; }
139 .conn-gstate { color: var(--ink-soft, #888); font-size: .82rem; }
140 .tl-wrap { max-width: 640px; margin: 1rem auto; padding: 0 1rem; }
141 .tl-title { margin: 0 0 .75rem; }
142 .tl-empty { color: var(--ink-soft, #888); }
143 .tl-follow { display: flex; gap: .5rem; margin: 0 0 1.25rem; flex-wrap: wrap; align-items: center; }
144 .tl-follow input[type="text"] { flex: 1; min-width: 200px; height: 2.4rem; padding: 0 .9rem; border-radius: 999px; font: inherit;
145 border: 1px solid color-mix(in srgb, var(--ink, #000) 18%, transparent); background: color-mix(in srgb, var(--ink, #000) 4%, transparent); color: var(--ink, #000); }
146 .tl-follow-ab { font-size: .85rem; color: var(--ink-soft, #888); display: inline-flex; align-items: center; gap: .3rem; cursor: pointer; }
147 .tl-foll-list { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: .4rem; }
148 .tl-foll { display: flex; align-items: center; gap: .6rem; padding: .45rem .6rem; border-radius: 12px; background: color-mix(in srgb, var(--ink, #000) 4%, transparent); }
149 .tl-foll.is-stale { background: color-mix(in srgb, #c0392b 10%, transparent); }
150 .tl-foll-av { flex: 0 0 36px; width: 36px; height: 36px; border-radius: 50%; overflow: hidden; display: inline-flex; align-items: center; justify-content: center; font-weight: 700; background: color-mix(in srgb, var(--accent, #888) 20%, transparent); color: var(--accent, #555); }
151 .tl-foll-av img { width: 100%; height: 100%; object-fit: cover; background: #fff; }
152 .tl-foll-meta { flex: 1; min-width: 0; display: flex; flex-direction: column; line-height: 1.3; }
153 .tl-foll-meta a { color: var(--ink, inherit); text-decoration: none; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
154 .tl-foll-meta a:hover { text-decoration: underline; }
155 .conn-sub { font-size: .78rem; color: var(--ink-soft, #888); display: inline-flex; flex-wrap: wrap; gap: .35rem; align-items: baseline; }
156 .conn-dir { font-weight: 600; }
157 .conn-dir--mutual { color: var(--accent, #06c); }
158 .conn-dir--following { color: color-mix(in srgb, var(--ink, #000) 62%, transparent); }
159 .conn-dir--follower { color: color-mix(in srgb, var(--ink, #000) 62%, transparent); }
160 .conn-never, .conn-err { color: #c0392b; }
161 .tl-foll-ab { margin: 0; font-size: .8rem; color: var(--ink-soft, #888); cursor: pointer; }
162 .tl-foll-ab label { display: inline-flex; align-items: center; gap: .25rem; cursor: pointer; }
163 .tl-unfollow { background: none; border: 1px solid color-mix(in srgb, var(--ink, #000) 18%, transparent); border-radius: 8px; padding: .25rem .6rem; font-size: .8rem; color: var(--ink-soft, #888); cursor: pointer; white-space: nowrap; }
164 .tl-unfollow:hover { color: var(--ink, #000); }
165 .conn-unreach { margin: 1.25rem 0 0; border-top: 1px solid color-mix(in srgb, var(--ink, #000) 10%, transparent); padding-top: .75rem; }
166 .conn-unreach summary { cursor: pointer; font-weight: 600; color: #c0392b; }
167 .conn-unreach-lead { font-size: .8rem; color: var(--ink-soft, #888); margin: .5rem 0 .75rem; }
168</style>
Note: See TracBrowser for help on using the repository browser.