source: Klonkt/src/views/pages/messages.ejs@ a78a0ef

main
Last change on this file since a78a0ef was a78a0ef, checked in by Claude (agent) <aiclaude@…>, 5 weeks ago

Draadkop: de tegenpartij in een kleine uitsparing

De naam van de tegenpartij stond als vette regel boven het gesprek en at daarmee
ruimte op die aan de inhoud toekomt. Het is een label bij de draad, geen kop
erboven, dus het gedraagt zich nu ook zo: een kleine verzonken uitsparing,
kleiner lettertype, gedempte kleur.

Het aantal berichten zit in diezelfde uitsparing in plaats van als tweede pil
ernaast, gescheiden door een dun lijntje. Scheelt een element en een gat.

Verder is de kop lager (marge .5 -> .35rem, tussenruimte .5 -> .4rem) en kapt
een lange deelnemerslijst af op 60% breedte, zodat de post-link rechts altijd
past.

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

  • Property mode set to 100644
File size: 15.1 KB
Line 
1<div class="msg-wrap">
2 <%- include('../partials/fedi-tabs', { active: 'berichten' }) %>
3 <h1 class="msg-title"><%= t('msg.title') %></h1>
4 <% if (typeof success !== 'undefined' && success) { %><div class="alert alert-success"><%= success === 'guardian_accepted' ? t('msg.guard_accepted') : (success === 'guardian_rejected' ? t('msg.guard_rejected') : (success === 'wave_sent' ? t('msg.wave_sent') : success)) %></div><% } %>
5 <% if (typeof error !== 'undefined' && error) { %><div class="alert alert-error"><%= error === 'guardianship' ? t('msg.guard_failed') : error %></div><% } %>
6
7 <%# FEP-633c: a pending guardianship offer is a special message: the kid
8 answers here (accept/reject travels the same C2S pipeline as the apps). %>
9 <% if (typeof guardianOffers !== 'undefined' && guardianOffers.length) { %>
10 <% guardianOffers.forEach(function(o){ %>
11 <div class="alert" style="display:flex;align-items:center;gap:12px;flex-wrap:wrap;border-left:3px solid #ff6b35;">
12 <span style="font-size:1.4em;">&#128735;</span>
13 <div style="flex:1;min-width:200px;">
14 <strong><%= o['shaer:candidateHandle'] || o['shaer:candidate'] %></strong><br>
15 <span><%= t('msg.guard_offer') %></span>
16 </div>
17 <form method="post" action="<%= (typeof moreBase !== 'undefined' ? moreBase : '') %>/messages/guardianship" style="display:flex;gap:8px;">
18 <input type="hidden" name="offer" value="<%= o.id %>">
19 <button class="btn" type="submit" name="answer" value="accept"><%= t('msg.guard_accept') %></button>
20 <button class="btn" type="submit" name="answer" value="reject" style="opacity:.7;"><%= t('msg.guard_reject') %></button>
21 </form>
22 </div>
23 <% }); %>
24 <% } %>
25
26 <div class="msg-filters" role="tablist" aria-label="<%= t('msg.title') %>">
27 <%# Vier chips, geen zes: Berichten, Gesprekken en Verzonden gingen op in
28 een enkele Gesprekken-view, waarin verzonden en ontvangen in dezelfde
29 draad staan. Activiteit en Moderatie blijven wat ze waren. %>
30 <button type="button" class="msg-chip is-on" data-show="all"><%= t('msg.filter_all') %></button>
31 <button type="button" class="msg-chip" data-show="conv"><%= t('msg.filter_conv') %></button>
32 <button type="button" class="msg-chip" data-show="act"><%= t('msg.filter_act') %></button>
33 <button type="button" class="msg-chip" data-show="mod"><%= t('msg.filter_mod') %></button>
34 </div>
35 <div class="msg-search">
36 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
37 <input type="search" id="msg-q" placeholder="<%= t('msg.search_ph') %>" autocomplete="off" spellcheck="false" aria-label="<%= t('msg.search_ph') %>">
38 </div>
39
40 <% var _seen = (typeof seenAt !== 'undefined' && seenAt) ? seenAt : 0; %>
41 <% if (!items || !items.length) { %>
42 <p class="msg-empty"><%= t('msg.empty') %></p>
43 <% } else { %>
44 <ul class="msg-list" data-show="all" id="msg-list">
45 <% items.forEach(function(n){ %><%- include('../partials/msg-item', { n: n, seen: _seen }) %><% }); %>
46 </ul>
47 <p class="msg-nomatch" hidden><%= t('msg.no_match') %></p>
48 <%- include('../partials/load-more', { hasMore: hasMore, nextOffset: nextOffset, moreBase: moreBase, moreTarget: '#msg-list', morePath: '/messages' }) %>
49 <% } %>
50
51 <%# Interact bookmarklet — moved here from the old Reacties page. %>
52 <details class="msg-bm">
53 <summary><%= t('fedi.bm_label') %></summary>
54 <p class="msg-bm-help"><%= t('fedi.bm_help') %></p>
55 <a class="fedi-bm-btn" id="fedi-bm-btn" href="#" draggable="true" title="<%= t('fedi.bm_help') %>">
56 <svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z"/></svg>
57 <%= t('fedi.bm_label') %>
58 </a>
59 </details>
60</div>
61
62<script>
63(function () {
64 if (window.__msgWired) return; window.__msgWired = true;
65
66 // Filtering: kind chip AND free-text search, combined in JS (search over the
67 // sender and the rendered message text). Empty search + "all" = show all.
68 var list = document.querySelector('.msg-list');
69 var noMatch = document.querySelector('.msg-nomatch');
70 var q = document.getElementById('msg-q');
71 var kind = 'all';
72 var items = [];
73 function indexItem(li) {
74 // Een draad indexeren op zijn eerste bubbel zou de rest onvindbaar maken,
75 // dus daar nemen we de hele tekst; losse regels blijven gericht geindexeerd.
76 if (li.classList.contains('msg-thread')) {
77 li._search = ((li.getAttribute('data-who') || '') + ' ' + li.textContent).toLowerCase();
78 return;
79 }
80 // Index once: sender + message body + linked post title + poll text.
81 var body = li.querySelector('.msg-content');
82 var post = li.querySelector('.msg-post');
83 var poll = li.querySelector('.msg-poll');
84 li._search = ((li.getAttribute('data-who') || '') + ' ' +
85 (body ? body.textContent : '') + ' ' + (post ? post.textContent : '') + ' ' +
86 (poll ? poll.textContent : '')).toLowerCase();
87 }
88 // Re-collect + index; called on load and after each "Load more" append so new
89 // items join the filter/search (and inherit the active chip via apply()).
90 function reindex() {
91 items = list ? Array.prototype.slice.call(list.querySelectorAll(':scope > .msg-item')) : [];
92 items.forEach(function (li) { if (!li._search) indexItem(li); });
93 }
94 reindex();
95 function apply() {
96 if (!list) return;
97 var term = (q && q.value || '').trim().toLowerCase();
98 var shown = 0;
99 items.forEach(function (li) {
100 var ok = (kind === 'all' || li.getAttribute('data-kind') === kind) &&
101 (!term || li._search.indexOf(term) !== -1);
102 li.style.display = ok ? '' : 'none';
103 if (ok) shown++;
104 });
105 if (noMatch) noMatch.hidden = shown !== 0;
106 }
107 document.addEventListener('click', function (e) {
108 var chip = e.target.closest('.msg-chip'); if (!chip) return;
109 kind = chip.getAttribute('data-show');
110 document.querySelectorAll('.msg-chip').forEach(function (c) { c.classList.toggle('is-on', c === chip); });
111 apply();
112 });
113 if (q) q.addEventListener('input', apply);
114 // After a "Load more" append (htmx), index the new rows and re-apply the filter.
115 document.body.addEventListener('htmx:afterSettle', function (e) {
116 if (e.target && e.target.id === 'msg-list') { reindex(); apply(); }
117 });
118
119 var a = document.getElementById('fedi-bm-btn');
120 if (a) {
121 a.setAttribute('href', "javascript:void(window.open('" + location.origin + "/authorize_interaction?uri='+encodeURIComponent(window.location.href)))");
122 a.addEventListener('click', function (e) { e.preventDefault(); a.classList.add('nudge'); setTimeout(function(){ a.classList.remove('nudge'); }, 600); });
123 }
124})();
125</script>
126
127<style>
128 .msg-wrap { max-width: 640px; margin: 1rem auto; padding: 0 1rem; }
129 .msg-title { margin: 0 0 .9rem; }
130 .msg-empty { color: var(--ink-soft, #888); }
131
132 .msg-filters { display: flex; gap: .4rem; margin: 0 0 1.1rem; flex-wrap: wrap; }
133 .msg-chip { border: 1px solid color-mix(in srgb, var(--ink, #000) 16%, transparent); background: none;
134 color: var(--ink-soft, #888); font: inherit; font-size: .84rem; font-weight: 600;
135 padding: .3rem .85rem; border-radius: 999px; cursor: pointer; }
136 .msg-chip.is-on { background: var(--accent, #06c); border-color: var(--accent, #06c); color: #fff; }
137
138 .msg-search { display: flex; align-items: center; gap: .5rem; margin: 0 0 1.1rem;
139 padding: .45rem .7rem; border-radius: 10px;
140 border: 1px solid color-mix(in srgb, var(--ink, #000) 16%, transparent); }
141 .msg-search:focus-within { border-color: var(--accent, #06c); box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent, #06c) 18%, transparent); }
142 .msg-search svg { width: 16px; height: 16px; color: var(--ink-soft, #888); flex-shrink: 0; }
143 .msg-search input { flex: 1; border: none; background: none; color: inherit; font: inherit; outline: none; min-width: 0; }
144 .msg-nomatch { color: var(--ink-soft, #888); text-align: center; padding: 1.2rem 0; }
145
146 .msg-list { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: .55rem; }
147
148 /* Een draad is geen regel maar een blok: kop met de context, daaronder de
149 bubbels van oud naar nieuw. De verticale lijn links maakt zichtbaar dat ze
150 bij elkaar horen -- dat was precies wat ontbrak toen verzonden en ontvangen
151 in aparte chips zaten. */
152 .msg-thread { display: block; }
153 .msg-thread-head { display: flex; align-items: center; gap: .4rem; flex-wrap: wrap; margin-bottom: .35rem; }
154 /* De tegenpartij is een labeltje bij het gesprek, geen kop erboven: een
155 kleine verzonken uitsparing, zodat de bubbels eronder de ruimte krijgen.
156 Het aantal zit in dezelfde uitsparing in plaats van ernaast. */
157 .msg-thread-who { display: inline-flex; align-items: center; gap: .3rem;
158 font-size: .78em; line-height: 1.5; font-weight: 500;
159 padding: .05rem .5rem; border-radius: 999px;
160 color: color-mix(in srgb, var(--ink, #000) 62%, transparent);
161 background: color-mix(in srgb, var(--ink, #000) 5%, transparent);
162 box-shadow: inset 0 1px 2px color-mix(in srgb, var(--ink, #000) 9%, transparent);
163 max-width: 60%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
164 .msg-thread-count { font-variant-numeric: tabular-nums; opacity: .75;
165 padding-inline-start: .3rem; border-inline-start: 1px solid color-mix(in srgb, var(--ink, #000) 14%, transparent); }
166 .msg-thread-post { display: inline-flex; align-items: center; gap: .3rem; font-size: .85em;
167 margin-inline-start: auto; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
168 .msg-thread-post svg { width: 13px; height: 13px; flex: none; }
169 .msg-thread-msgs { list-style: none; margin: 0; padding: 0 0 0 .85rem; display: flex; flex-direction: column; gap: .45rem;
170 border-inline-start: 2px solid color-mix(in srgb, var(--ink, #000) 10%, transparent); }
171 .msg-sub { display: flex; gap: .8rem; align-items: flex-start; }
172 /* Jouw eigen bijdrage staat aan de andere kant van de lijn, zodat je de
173 beurten ziet zonder namen te lezen. */
174 .msg-sub.msg-sent { flex-direction: row-reverse; text-align: end; }
175 .msg-sub.msg-sent .msg-line { justify-content: flex-end; }
176
177 .msg-item { display: flex; gap: .8rem; padding: .85rem .95rem; border-radius: 14px; align-items: flex-start;
178 background: color-mix(in srgb, var(--ink, #000) 3.5%, transparent);
179 border: 1px solid color-mix(in srgb, var(--ink, #000) 9%, transparent);
180 transition: border-color .15s ease, box-shadow .15s ease; }
181 .msg-item:hover { border-color: color-mix(in srgb, var(--accent, #888) 30%, transparent);
182 box-shadow: 0 2px 12px color-mix(in srgb, var(--ink, #000) 6%, transparent); }
183 .msg-item.is-new { border-color: color-mix(in srgb, var(--accent, #06c) 45%, transparent);
184 background: color-mix(in srgb, var(--accent, #06c) 4%, transparent); }
185 .msg-sent { border-left: 3px solid color-mix(in srgb, var(--accent, #06c) 55%, transparent); }
186
187 .msg-av { position: relative; flex: 0 0 40px; width: 40px; height: 40px; border-radius: 50%;
188 display: inline-flex; align-items: center; justify-content: center; font-weight: 700;
189 background: color-mix(in srgb, var(--accent, #888) 16%, transparent); color: var(--accent, #06c); overflow: visible; }
190 .msg-av img { width: 100%; height: 100%; object-fit: cover; border-radius: 50%; background: #fff; }
191 .msg-av-sent svg { width: 17px; height: 17px; }
192 .msg-dot { position: absolute; right: -3px; bottom: -3px; width: 18px; height: 18px; border-radius: 50%;
193 display: inline-flex; align-items: center; justify-content: center;
194 border: 2px solid var(--paper, #fff); background: var(--accent, #06c); color: #fff; }
195 .msg-dot svg { width: 10px; height: 10px; }
196 .msg-dot-like { background: #e8b04b; } .msg-dot-boost { background: #2fa85a; }
197 .msg-dot-report { background: #c0392b; } .msg-dot-mention { background: #8e6cf0; }
198
199 .msg-body { flex: 1; min-width: 0; }
200 .msg-line { display: flex; align-items: baseline; gap: .4rem; }
201 .msg-who { font-weight: 700; color: var(--ink, inherit); text-decoration: none; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
202 a.msg-who:hover { text-decoration: underline; }
203 .msg-handle { color: var(--ink-soft, #888); font-size: .82rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }
204 .msg-new { width: 8px; height: 8px; border-radius: 50%; background: var(--accent, #06c); flex: 0 0 auto; align-self: center; }
205 .msg-time { color: var(--ink-soft, #999); font-size: .76rem; margin-left: auto; flex: 0 0 auto; white-space: nowrap; }
206 .msg-what { color: var(--ink-soft, #aaa); margin-top: .12rem; line-height: 1.4; }
207 .msg-what .msg-post { color: var(--accent, #06c); font-weight: 600; margin-left: .25rem; }
208 .msg-priv { display: inline-block; margin-left: .3rem; padding: .05rem .5rem; border-radius: 999px; font-size: .72rem; font-weight: 700;
209 background: color-mix(in srgb, #8e6cf0 14%, transparent); color: #8e6cf0; }
210 .msg-content { margin: .45rem 0 0; padding: .55rem .75rem; border-radius: 10px; line-height: 1.5;
211 background: color-mix(in srgb, var(--ink, #000) 4%, transparent); color: var(--ink, inherit); overflow-wrap: anywhere; }
212 .msg-content p { margin: .2rem 0; } .msg-content p:first-child { margin-top: 0; } .msg-content p:last-child { margin-bottom: 0; }
213
214 .msg-poll { margin: .5rem 0 0; display: flex; flex-direction: column; gap: .4rem; }
215 .msg-poll-opt { display: flex; flex-direction: column; gap: .2rem; }
216 .msg-poll-top { display: flex; justify-content: space-between; gap: .5rem; font-size: .85rem; }
217 .msg-poll-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
218 .msg-poll-pct { font-variant-numeric: tabular-nums; color: var(--ink-soft, #888); flex-shrink: 0; }
219 .msg-poll-bar { height: 7px; border-radius: 999px; overflow: hidden; background: color-mix(in srgb, var(--ink, #000) 10%, transparent); }
220 .msg-poll-bar span { display: block; height: 100%; border-radius: 999px; background: var(--accent, #06c); }
221 .msg-poll-total { font-size: .78rem; color: var(--ink-soft, #888); margin-top: .1rem; }
222
223 .msg-actions { display: flex; gap: .7rem; align-items: center; margin-top: .45rem; }
224 .msg-edit summary { cursor: pointer; font-size: .8rem; color: var(--ink-soft, #888); font-weight: 600; }
225 .msg-edit-form { margin-top: .4rem; display: flex; flex-direction: column; gap: .4rem; }
226 .msg-edit-form textarea { width: 100%; font: inherit; padding: .5rem .65rem; border-radius: 8px;
227 border: 1px solid color-mix(in srgb, var(--ink, #000) 16%, transparent); background: transparent; color: var(--ink, inherit); }
228 .msg-del { background: none; border: none; padding: 0; font: inherit; font-size: .8rem; font-weight: 600;
229 color: var(--ink-soft, #888); cursor: pointer; }
230 .msg-del:hover { color: #c0392b; }
231
232 .msg-bm { margin: 1.4rem 0 0; border-top: 1px solid color-mix(in srgb, var(--ink, #000) 10%, transparent); padding-top: .8rem; }
233 .msg-bm summary { cursor: pointer; color: var(--ink-soft, #888); font-weight: 600; font-size: .88rem; }
234 .msg-bm-help { color: var(--ink-soft, #888); font-size: .82rem; line-height: 1.5; margin: .5rem 0 .6rem; }
235</style>
Note: See TracBrowser for help on using the repository browser.