Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/views/pages/messages.ejs

    re64a49b r439f095  
    22  <%- include('../partials/fedi-tabs', { active: 'berichten' }) %>
    33  <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 === 'reply_sent' ? t('msg.reply_sent') : success))) %></div><% } %>
    5   <% if (typeof error !== 'undefined' && error) { %><div class="alert alert-error"><%= error === 'guardianship' ? t('msg.guard_failed') : (error === 'reply_empty' ? t('msg.reply_empty') : ((error === 'reply_failed' || error === 'reply_target') ? t('msg.reply_failed') : error)) %></div><% } %>
     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><% } %>
    66
    77  <%# FEP-633c: a pending guardianship offer is a special message: the kid
     
    2525
    2626  <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. %>
    3027    <button type="button" class="msg-chip is-on" data-show="all"><%= t('msg.filter_all') %></button>
     28    <button type="button" class="msg-chip" data-show="msgs"><%= t('msg.filter_msgs') %></button>
    3129    <button type="button" class="msg-chip" data-show="conv"><%= t('msg.filter_conv') %></button>
    3230    <button type="button" class="msg-chip" data-show="act"><%= t('msg.filter_act') %></button>
    3331    <button type="button" class="msg-chip" data-show="mod"><%= t('msg.filter_mod') %></button>
     32    <button type="button" class="msg-chip" data-show="sent"><%= t('msg.filter_sent') %></button>
    3433  </div>
    3534  <div class="msg-search">
     
    6059</div>
    6160
    62 <%# Filteren, zoeken en het in-/uitklappen zitten in assets/js/mod/messages.js.
    63     Inline script hier wordt door de CSP geweigerd zodra je deze pagina via een
    64     link BINNEN de site opent -- zie shaer-0i6. De shell laadt de module op
    65     body[data-js]; deze pagina vraagt erom met pageJs (routes/posts.js). %>
     61<script>
     62(function () {
     63  if (window.__msgWired) return; window.__msgWired = true;
     64
     65  // Filtering: kind chip AND free-text search, combined in JS (search over the
     66  // sender and the rendered message text). Empty search + "all" = show all.
     67  var list = document.querySelector('.msg-list');
     68  var noMatch = document.querySelector('.msg-nomatch');
     69  var q = document.getElementById('msg-q');
     70  var kind = 'all';
     71  var items = [];
     72  function indexItem(li) {
     73    // Index once: sender + message body + linked post title + poll text.
     74    var body = li.querySelector('.msg-content');
     75    var post = li.querySelector('.msg-post');
     76    var poll = li.querySelector('.msg-poll');
     77    li._search = ((li.getAttribute('data-who') || '') + ' ' +
     78      (body ? body.textContent : '') + ' ' + (post ? post.textContent : '') + ' ' +
     79      (poll ? poll.textContent : '')).toLowerCase();
     80  }
     81  // Re-collect + index; called on load and after each "Load more" append so new
     82  // items join the filter/search (and inherit the active chip via apply()).
     83  function reindex() {
     84    items = list ? Array.prototype.slice.call(list.querySelectorAll('.msg-item')) : [];
     85    items.forEach(function (li) { if (!li._search) indexItem(li); });
     86  }
     87  reindex();
     88  function apply() {
     89    if (!list) return;
     90    var term = (q && q.value || '').trim().toLowerCase();
     91    var shown = 0;
     92    items.forEach(function (li) {
     93      var ok = (kind === 'all' || li.getAttribute('data-kind') === kind) &&
     94        (!term || li._search.indexOf(term) !== -1);
     95      li.style.display = ok ? '' : 'none';
     96      if (ok) shown++;
     97    });
     98    if (noMatch) noMatch.hidden = shown !== 0;
     99  }
     100  document.addEventListener('click', function (e) {
     101    var chip = e.target.closest('.msg-chip'); if (!chip) return;
     102    kind = chip.getAttribute('data-show');
     103    document.querySelectorAll('.msg-chip').forEach(function (c) { c.classList.toggle('is-on', c === chip); });
     104    apply();
     105  });
     106  if (q) q.addEventListener('input', apply);
     107  // After a "Load more" append (htmx), index the new rows and re-apply the filter.
     108  document.body.addEventListener('htmx:afterSettle', function (e) {
     109    if (e.target && e.target.id === 'msg-list') { reindex(); apply(); }
     110  });
     111
     112  var a = document.getElementById('fedi-bm-btn');
     113  if (a) {
     114    a.setAttribute('href', "javascript:void(window.open('" + location.origin + "/authorize_interaction?uri='+encodeURIComponent(window.location.href)))");
     115    a.addEventListener('click', function (e) { e.preventDefault(); a.classList.add('nudge'); setTimeout(function(){ a.classList.remove('nudge'); }, 600); });
     116  }
     117})();
     118</script>
    66119
    67120<style>
     
    86139  .msg-list { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: .55rem; }
    87140
    88 
    89141  .msg-item { display: flex; gap: .8rem; padding: .85rem .95rem; border-radius: 14px; align-items: flex-start;
    90142    background: color-mix(in srgb, var(--ink, #000) 3.5%, transparent);
     
    96148    background: color-mix(in srgb, var(--accent, #06c) 4%, transparent); }
    97149  .msg-sent { border-left: 3px solid color-mix(in srgb, var(--accent, #06c) 55%, transparent); }
    98 
    99   /* ── Gesprekken ──────────────────────────────────────────────────────
    100      NA .msg-item, en dat is geen smaak: .msg-item zet display:flex, en bij
    101      gelijke specificiteit wint de laatste regel. Stond dit ervoor, dan kwam de
    102      draadkop NAAST de bubbels te staan in plaats van erboven. */
    103   .msg-thread { display: block; }
    104   .msg-thread-head { display: flex; align-items: center; gap: .4rem; flex-wrap: wrap; margin-bottom: .4rem; }
    105   /* De tegenpartij is een labeltje bij het gesprek, geen kop erboven: een
    106      kleine verzonken uitsparing, zodat de bubbels eronder de ruimte krijgen.
    107      Het aantal zit in dezelfde uitsparing in plaats van ernaast. */
    108   /* Ook de knop die het gesprek in- en uitklapt. Een button erft geen font en
    109      geen kleur, dus die staan hier expliciet; zonder dat wordt het ineens
    110      systeemblauw in 13px. */
    111   .msg-thread-who { display: inline-flex; align-items: center; gap: .3rem; min-width: 0;
    112     font: inherit; font-size: .78em; line-height: 1.5; font-weight: 500;
    113     padding: .05rem .5rem; border-radius: 999px; border: 0; cursor: pointer;
    114     color: color-mix(in srgb, var(--ink, #000) 62%, transparent);
    115     background: color-mix(in srgb, var(--ink, #000) 5%, transparent);
    116     box-shadow: inset 0 1px 2px color-mix(in srgb, var(--ink, #000) 9%, transparent);
    117     max-width: 100%; overflow: hidden; white-space: nowrap;
    118     /* Ruimer aanraakvlak dan de tekst: een chip van 18px hoog is op een telefoon
    119        niet te raken. De uitsparing blijft klein, het doel wordt groter. */
    120     min-height: 32px; }
    121   .msg-thread-who:hover { background: color-mix(in srgb, var(--ink, #000) 9%, transparent); }
    122   .msg-thread-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    123   .msg-thread-chevron { width: 12px; height: 12px; flex: none; opacity: .55;
    124     transition: transform .18s ease; }
    125   /* Ingeklapt: de bubbels en het antwoordveld verdwijnen, de uitsparing met de
    126      naam en het AANTAL blijft staan -- dat aantal is juist ingeklapt het meest
    127      waard. */
    128   .msg-thread.is-collapsed .msg-thread-msgs,
    129   .msg-thread.is-collapsed .msg-thread-foot { display: none; }
    130   /* Tijdens het zoeken wint de treffer van de dichtgeklapte stand. */
    131   .msg-thread.is-collapsed.is-search-open .msg-thread-msgs { display: flex; }
    132   .msg-thread.is-collapsed.is-search-open .msg-thread-foot { display: flex; }
    133   .msg-thread.is-collapsed .msg-thread-chevron { transform: rotate(-90deg); }
    134   .msg-thread.is-collapsed .msg-thread-head { margin-bottom: 0; }
    135   @media (prefers-reduced-motion: reduce) { .msg-thread-chevron { transition: none; } }
    136   .msg-thread-count { font-variant-numeric: tabular-nums; opacity: .75; flex: 0 0 auto;
    137     padding-inline-start: .3rem; border-inline-start: 1px solid color-mix(in srgb, var(--ink, #000) 14%, transparent); }
    138   .msg-thread-post { display: inline-flex; align-items: center; gap: .3rem; font-size: .85em;
    139     margin-inline-start: auto; min-width: 0; max-width: 100%;
    140     overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    141   .msg-thread-post svg { width: 13px; height: 13px; flex: none; }
    142   .msg-thread-msgs { list-style: none; margin: 0; padding: 0 0 0 .7rem; display: flex; flex-direction: column; gap: .5rem;
    143     border-inline-start: 2px solid color-mix(in srgb, var(--ink, #000) 10%, transparent); }
    144   .msg-sub { display: flex; gap: .6rem; align-items: flex-start; min-width: 0; }
    145   .msg-sub > .msg-body { min-width: 0; }
    146   /* Jouw eigen bijdrage onderscheidt zich met een tint en de accentlijn die
    147      .msg-sent al had -- niet met row-reverse: dat duwde de avatar buiten de
    148      kaart zodra de regel smaller werd dan zijn inhoud. */
    149   .msg-sub.msg-sent { border-radius: 10px; padding: .3rem .45rem;
    150     background: color-mix(in srgb, var(--accent, #06c) 5%, transparent); }
    151   /* Een lange naam of tijd mag de bubbel niet uit de kaart duwen. */
    152   .msg-sub .msg-line { flex-wrap: wrap; }
    153 
    154   /* De voet van een gesprek: antwoorden en zwaaien naast elkaar, ingeklapt.
    155      Een open editor onder elk gesprek maakt de lijst weer onleesbaar -- precies
    156      wat deze weergave moest oplossen. */
    157   .msg-thread-foot { display: flex; align-items: flex-start; gap: .5rem; margin-top: .5rem; flex-wrap: wrap; }
    158   .msg-reply { flex: 1 1 12rem; min-width: 0; }
    159   .msg-reply > summary { cursor: pointer; font-size: .82rem; color: var(--ink-soft, #888); }
    160   .msg-reply[open] > summary { margin-bottom: .4rem; }
    161   .msg-reply-form { min-width: 0; }
    162   .msg-wave button { line-height: 1.2; }
    163 
    164   @media (max-width: 560px) {
    165     /* Op een telefoon is 40px avatar per bubbel puur verlies: de naam staat er
    166        al naast, en de inhoud heeft de breedte harder nodig. */
    167     .msg-sub > .msg-av { display: none; }
    168     .msg-thread-msgs { padding-inline-start: .55rem; }
    169     .msg-thread-post { margin-inline-start: 0; }
    170     .msg-item { padding: .7rem .6rem; gap: .6rem; }
    171   }
    172150
    173151  .msg-av { position: relative; flex: 0 0 40px; width: 40px; height: 40px; border-radius: 50%;
Note: See TracChangeset for help on using the changeset viewer.