| 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) %></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;">🛟</span>
|
|---|
| 13 | <div style="flex:1;min-width:200px;">
|
|---|
| 14 | <strong><%= o.other_handle || o.other_uri %></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="guardian" value="<%= o.other_uri %>">
|
|---|
| 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 | <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>
|
|---|
| 29 | <button type="button" class="msg-chip" data-show="conv"><%= t('msg.filter_conv') %></button>
|
|---|
| 30 | <button type="button" class="msg-chip" data-show="act"><%= t('msg.filter_act') %></button>
|
|---|
| 31 | <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>
|
|---|
| 33 | </div>
|
|---|
| 34 | <div class="msg-search">
|
|---|
| 35 | <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>
|
|---|
| 36 | <input type="search" id="msg-q" placeholder="<%= t('msg.search_ph') %>" autocomplete="off" spellcheck="false" aria-label="<%= t('msg.search_ph') %>">
|
|---|
| 37 | </div>
|
|---|
| 38 |
|
|---|
| 39 | <% var _seen = (typeof seenAt !== 'undefined' && seenAt) ? seenAt : 0; %>
|
|---|
| 40 | <% if (!items || !items.length) { %>
|
|---|
| 41 | <p class="msg-empty"><%= t('msg.empty') %></p>
|
|---|
| 42 | <% } else { %>
|
|---|
| 43 | <ul class="msg-list" data-show="all" id="msg-list">
|
|---|
| 44 | <% items.forEach(function(n){ %><%- include('../partials/msg-item', { n: n, seen: _seen }) %><% }); %>
|
|---|
| 45 | </ul>
|
|---|
| 46 | <p class="msg-nomatch" hidden><%= t('msg.no_match') %></p>
|
|---|
| 47 | <%- include('../partials/load-more', { hasMore: hasMore, nextOffset: nextOffset, moreBase: moreBase, moreTarget: '#msg-list', morePath: '/messages' }) %>
|
|---|
| 48 | <% } %>
|
|---|
| 49 |
|
|---|
| 50 | <%# Interact bookmarklet — moved here from the old Reacties page. %>
|
|---|
| 51 | <details class="msg-bm">
|
|---|
| 52 | <summary><%= t('fedi.bm_label') %></summary>
|
|---|
| 53 | <p class="msg-bm-help"><%= t('fedi.bm_help') %></p>
|
|---|
| 54 | <a class="fedi-bm-btn" id="fedi-bm-btn" href="#" draggable="true" title="<%= t('fedi.bm_help') %>">
|
|---|
| 55 | <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>
|
|---|
| 56 | <%= t('fedi.bm_label') %>
|
|---|
| 57 | </a>
|
|---|
| 58 | </details>
|
|---|
| 59 | </div>
|
|---|
| 60 |
|
|---|
| 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>
|
|---|
| 119 |
|
|---|
| 120 | <style>
|
|---|
| 121 | .msg-wrap { max-width: 640px; margin: 1rem auto; padding: 0 1rem; }
|
|---|
| 122 | .msg-title { margin: 0 0 .9rem; }
|
|---|
| 123 | .msg-empty { color: var(--ink-soft, #888); }
|
|---|
| 124 |
|
|---|
| 125 | .msg-filters { display: flex; gap: .4rem; margin: 0 0 1.1rem; flex-wrap: wrap; }
|
|---|
| 126 | .msg-chip { border: 1px solid color-mix(in srgb, var(--ink, #000) 16%, transparent); background: none;
|
|---|
| 127 | color: var(--ink-soft, #888); font: inherit; font-size: .84rem; font-weight: 600;
|
|---|
| 128 | padding: .3rem .85rem; border-radius: 999px; cursor: pointer; }
|
|---|
| 129 | .msg-chip.is-on { background: var(--accent, #06c); border-color: var(--accent, #06c); color: #fff; }
|
|---|
| 130 |
|
|---|
| 131 | .msg-search { display: flex; align-items: center; gap: .5rem; margin: 0 0 1.1rem;
|
|---|
| 132 | padding: .45rem .7rem; border-radius: 10px;
|
|---|
| 133 | border: 1px solid color-mix(in srgb, var(--ink, #000) 16%, transparent); }
|
|---|
| 134 | .msg-search:focus-within { border-color: var(--accent, #06c); box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent, #06c) 18%, transparent); }
|
|---|
| 135 | .msg-search svg { width: 16px; height: 16px; color: var(--ink-soft, #888); flex-shrink: 0; }
|
|---|
| 136 | .msg-search input { flex: 1; border: none; background: none; color: inherit; font: inherit; outline: none; min-width: 0; }
|
|---|
| 137 | .msg-nomatch { color: var(--ink-soft, #888); text-align: center; padding: 1.2rem 0; }
|
|---|
| 138 |
|
|---|
| 139 | .msg-list { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: .55rem; }
|
|---|
| 140 |
|
|---|
| 141 | .msg-item { display: flex; gap: .8rem; padding: .85rem .95rem; border-radius: 14px; align-items: flex-start;
|
|---|
| 142 | background: color-mix(in srgb, var(--ink, #000) 3.5%, transparent);
|
|---|
| 143 | border: 1px solid color-mix(in srgb, var(--ink, #000) 9%, transparent);
|
|---|
| 144 | transition: border-color .15s ease, box-shadow .15s ease; }
|
|---|
| 145 | .msg-item:hover { border-color: color-mix(in srgb, var(--accent, #888) 30%, transparent);
|
|---|
| 146 | box-shadow: 0 2px 12px color-mix(in srgb, var(--ink, #000) 6%, transparent); }
|
|---|
| 147 | .msg-item.is-new { border-color: color-mix(in srgb, var(--accent, #06c) 45%, transparent);
|
|---|
| 148 | background: color-mix(in srgb, var(--accent, #06c) 4%, transparent); }
|
|---|
| 149 | .msg-sent { border-left: 3px solid color-mix(in srgb, var(--accent, #06c) 55%, transparent); }
|
|---|
| 150 |
|
|---|
| 151 | .msg-av { position: relative; flex: 0 0 40px; width: 40px; height: 40px; border-radius: 50%;
|
|---|
| 152 | display: inline-flex; align-items: center; justify-content: center; font-weight: 700;
|
|---|
| 153 | background: color-mix(in srgb, var(--accent, #888) 16%, transparent); color: var(--accent, #06c); overflow: visible; }
|
|---|
| 154 | .msg-av img { width: 100%; height: 100%; object-fit: cover; border-radius: 50%; background: #fff; }
|
|---|
| 155 | .msg-av-sent svg { width: 17px; height: 17px; }
|
|---|
| 156 | .msg-dot { position: absolute; right: -3px; bottom: -3px; width: 18px; height: 18px; border-radius: 50%;
|
|---|
| 157 | display: inline-flex; align-items: center; justify-content: center;
|
|---|
| 158 | border: 2px solid var(--paper, #fff); background: var(--accent, #06c); color: #fff; }
|
|---|
| 159 | .msg-dot svg { width: 10px; height: 10px; }
|
|---|
| 160 | .msg-dot-like { background: #e8b04b; } .msg-dot-boost { background: #2fa85a; }
|
|---|
| 161 | .msg-dot-report { background: #c0392b; } .msg-dot-mention { background: #8e6cf0; }
|
|---|
| 162 |
|
|---|
| 163 | .msg-body { flex: 1; min-width: 0; }
|
|---|
| 164 | .msg-line { display: flex; align-items: baseline; gap: .4rem; }
|
|---|
| 165 | .msg-who { font-weight: 700; color: var(--ink, inherit); text-decoration: none; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|---|
| 166 | a.msg-who:hover { text-decoration: underline; }
|
|---|
| 167 | .msg-handle { color: var(--ink-soft, #888); font-size: .82rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }
|
|---|
| 168 | .msg-new { width: 8px; height: 8px; border-radius: 50%; background: var(--accent, #06c); flex: 0 0 auto; align-self: center; }
|
|---|
| 169 | .msg-time { color: var(--ink-soft, #999); font-size: .76rem; margin-left: auto; flex: 0 0 auto; white-space: nowrap; }
|
|---|
| 170 | .msg-what { color: var(--ink-soft, #aaa); margin-top: .12rem; line-height: 1.4; }
|
|---|
| 171 | .msg-what .msg-post { color: var(--accent, #06c); font-weight: 600; margin-left: .25rem; }
|
|---|
| 172 | .msg-priv { display: inline-block; margin-left: .3rem; padding: .05rem .5rem; border-radius: 999px; font-size: .72rem; font-weight: 700;
|
|---|
| 173 | background: color-mix(in srgb, #8e6cf0 14%, transparent); color: #8e6cf0; }
|
|---|
| 174 | .msg-content { margin: .45rem 0 0; padding: .55rem .75rem; border-radius: 10px; line-height: 1.5;
|
|---|
| 175 | background: color-mix(in srgb, var(--ink, #000) 4%, transparent); color: var(--ink, inherit); overflow-wrap: anywhere; }
|
|---|
| 176 | .msg-content p { margin: .2rem 0; } .msg-content p:first-child { margin-top: 0; } .msg-content p:last-child { margin-bottom: 0; }
|
|---|
| 177 |
|
|---|
| 178 | .msg-poll { margin: .5rem 0 0; display: flex; flex-direction: column; gap: .4rem; }
|
|---|
| 179 | .msg-poll-opt { display: flex; flex-direction: column; gap: .2rem; }
|
|---|
| 180 | .msg-poll-top { display: flex; justify-content: space-between; gap: .5rem; font-size: .85rem; }
|
|---|
| 181 | .msg-poll-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|---|
| 182 | .msg-poll-pct { font-variant-numeric: tabular-nums; color: var(--ink-soft, #888); flex-shrink: 0; }
|
|---|
| 183 | .msg-poll-bar { height: 7px; border-radius: 999px; overflow: hidden; background: color-mix(in srgb, var(--ink, #000) 10%, transparent); }
|
|---|
| 184 | .msg-poll-bar span { display: block; height: 100%; border-radius: 999px; background: var(--accent, #06c); }
|
|---|
| 185 | .msg-poll-total { font-size: .78rem; color: var(--ink-soft, #888); margin-top: .1rem; }
|
|---|
| 186 |
|
|---|
| 187 | .msg-actions { display: flex; gap: .7rem; align-items: center; margin-top: .45rem; }
|
|---|
| 188 | .msg-edit summary { cursor: pointer; font-size: .8rem; color: var(--ink-soft, #888); font-weight: 600; }
|
|---|
| 189 | .msg-edit-form { margin-top: .4rem; display: flex; flex-direction: column; gap: .4rem; }
|
|---|
| 190 | .msg-edit-form textarea { width: 100%; font: inherit; padding: .5rem .65rem; border-radius: 8px;
|
|---|
| 191 | border: 1px solid color-mix(in srgb, var(--ink, #000) 16%, transparent); background: transparent; color: var(--ink, inherit); }
|
|---|
| 192 | .msg-del { background: none; border: none; padding: 0; font: inherit; font-size: .8rem; font-weight: 600;
|
|---|
| 193 | color: var(--ink-soft, #888); cursor: pointer; }
|
|---|
| 194 | .msg-del:hover { color: #c0392b; }
|
|---|
| 195 |
|
|---|
| 196 | .msg-bm { margin: 1.4rem 0 0; border-top: 1px solid color-mix(in srgb, var(--ink, #000) 10%, transparent); padding-top: .8rem; }
|
|---|
| 197 | .msg-bm summary { cursor: pointer; color: var(--ink-soft, #888); font-weight: 600; font-size: .88rem; }
|
|---|
| 198 | .msg-bm-help { color: var(--ink-soft, #888); font-size: .82rem; line-height: 1.5; margin: .5rem 0 .6rem; }
|
|---|
| 199 | </style>
|
|---|