| 1 | // Berichten: filteren, zoeken, en gesprekken in- en uitklappen (shaer-bqr).
|
|---|
| 2 | //
|
|---|
| 3 | // Dit stond inline in pages/messages.ejs. Dat werkte alleen na een volledige
|
|---|
| 4 | // laadbeurt: kwam je hier via een link BINNEN de site, dan arriveerde het script
|
|---|
| 5 | // via htmx met een nonce die het document niet kent, en weigerde de CSP het
|
|---|
| 6 | // (shaer-0i6). Chips, zoeken en het inklappen deden dan niets, en de reply-editor
|
|---|
| 7 | // laadde niet -- dat was Barts melding.
|
|---|
| 8 | //
|
|---|
| 9 | // TWEE DINGEN VERANDERD bij de verhuizing, en ze zijn allebei nodig omdat een
|
|---|
| 10 | // module ANDERS leeft dan een inline script:
|
|---|
| 11 | //
|
|---|
| 12 | // NIETS VASTHOUDEN het inline script pakte .msg-list, #msg-q en .msg-nomatch
|
|---|
| 13 | // een keer bij het inladen. Een module wordt per document
|
|---|
| 14 | // maar EEN keer geimporteerd, dus als je Berichten verlaat
|
|---|
| 15 | // en terugkomt wijzen die naar elementen die er niet meer
|
|---|
| 16 | // zijn. Nu wordt er per keer opgezocht.
|
|---|
| 17 | // OPNIEUW INDEXEREN bij binnenkomst op deze pagina, niet alleen bij "meer
|
|---|
| 18 | // laden". Anders is de lijst na een navigatie niet
|
|---|
| 19 | // geindexeerd en filtert het zoeken op niets.
|
|---|
| 20 | //
|
|---|
| 21 | // Alles gedelegeerd op document, met een slot: de module kan een tweede keer
|
|---|
| 22 | // geladen worden en mag dan geen tweede stel luisteraars neerzetten.
|
|---|
| 23 |
|
|---|
| 24 | (function () {
|
|---|
| 25 | 'use strict';
|
|---|
| 26 | if (window.__msgWired) return;
|
|---|
| 27 | window.__msgWired = true;
|
|---|
| 28 |
|
|---|
| 29 | var kind = 'all';
|
|---|
| 30 | var items = [];
|
|---|
| 31 |
|
|---|
| 32 | // Per keer opzoeken. Zie de kop: vasthouden overleeft een navigatie niet.
|
|---|
| 33 | function list() { return document.querySelector('.msg-list'); }
|
|---|
| 34 | function noMatch() { return document.querySelector('.msg-nomatch'); }
|
|---|
| 35 | function query() { return document.getElementById('msg-q'); }
|
|---|
| 36 |
|
|---|
| 37 | function indexItem(li) {
|
|---|
| 38 | // Een draad indexeren op zijn eerste bubbel zou de rest onvindbaar maken,
|
|---|
| 39 | // dus daar nemen we de hele tekst; losse regels blijven gericht geindexeerd.
|
|---|
| 40 | if (li.classList.contains('msg-thread')) {
|
|---|
| 41 | li._search = ((li.getAttribute('data-who') || '') + ' ' + li.textContent).toLowerCase();
|
|---|
| 42 | return;
|
|---|
| 43 | }
|
|---|
| 44 | var body = li.querySelector('.msg-content');
|
|---|
| 45 | var post = li.querySelector('.msg-post');
|
|---|
| 46 | var poll = li.querySelector('.msg-poll');
|
|---|
| 47 | li._search = ((li.getAttribute('data-who') || '') + ' ' +
|
|---|
| 48 | (body ? body.textContent : '') + ' ' + (post ? post.textContent : '') + ' ' +
|
|---|
| 49 | (poll ? poll.textContent : '')).toLowerCase();
|
|---|
| 50 | }
|
|---|
| 51 |
|
|---|
| 52 | function reindex() {
|
|---|
| 53 | var l = list();
|
|---|
| 54 | items = l ? Array.prototype.slice.call(l.querySelectorAll(':scope > .msg-item')) : [];
|
|---|
| 55 | items.forEach(function (li) { if (!li._search) indexItem(li); });
|
|---|
| 56 | }
|
|---|
| 57 |
|
|---|
| 58 | function apply() {
|
|---|
| 59 | if (!list()) return;
|
|---|
| 60 | var qEl = query();
|
|---|
| 61 | var term = (qEl && qEl.value || '').trim().toLowerCase();
|
|---|
| 62 | var shown = 0;
|
|---|
| 63 | items.forEach(function (li) {
|
|---|
| 64 | var ok = (kind === 'all' || li.getAttribute('data-kind') === kind) &&
|
|---|
| 65 | (!term || li._search.indexOf(term) !== -1);
|
|---|
| 66 | li.style.display = ok ? '' : 'none';
|
|---|
| 67 | // Een treffer die in een dichtgeklapt gesprek zit, laat anders alleen de
|
|---|
| 68 | // naam zien: je zoekt iets, het staat er, en je ziet het niet. Zolang er
|
|---|
| 69 | // gezocht wordt gaat zo'n gesprek open; daarna keert hij terug naar de
|
|---|
| 70 | // stand die de lezer zelf koos.
|
|---|
| 71 | if (li.classList.contains('msg-thread')) {
|
|---|
| 72 | li.classList.toggle('is-search-open', !!term && ok);
|
|---|
| 73 | }
|
|---|
| 74 | if (ok) shown++;
|
|---|
| 75 | });
|
|---|
| 76 | var nm = noMatch();
|
|---|
| 77 | if (nm) nm.hidden = shown !== 0;
|
|---|
| 78 | }
|
|---|
| 79 |
|
|---|
| 80 | // In- en uitklappen door op de tegenpartij te tikken. De stand leeft alleen in
|
|---|
| 81 | // deze pagina: standaard uitgeklapt, en na een herlading weer. Dat is bewust --
|
|---|
| 82 | // een gesprek dat je gisteren dichtklapte stilhouden is niet hetzelfde als het
|
|---|
| 83 | // opruimen van je scherm van nu.
|
|---|
| 84 | document.addEventListener('click', function (e) {
|
|---|
| 85 | var who = e.target.closest && e.target.closest('.msg-thread-who'); if (!who) return;
|
|---|
| 86 | var li = who.closest('.msg-thread'); if (!li) return;
|
|---|
| 87 | var collapsed = li.classList.toggle('is-collapsed');
|
|---|
| 88 | who.setAttribute('aria-expanded', collapsed ? 'false' : 'true');
|
|---|
| 89 | });
|
|---|
| 90 |
|
|---|
| 91 | document.addEventListener('click', function (e) {
|
|---|
| 92 | var chip = e.target.closest && e.target.closest('.msg-chip'); if (!chip) return;
|
|---|
| 93 | kind = chip.getAttribute('data-show');
|
|---|
| 94 | document.querySelectorAll('.msg-chip').forEach(function (c) { c.classList.toggle('is-on', c === chip); });
|
|---|
| 95 | apply();
|
|---|
| 96 | });
|
|---|
| 97 |
|
|---|
| 98 | // Gedelegeerd in plaats van op het invoerveld zelf: dat veld wordt bij een
|
|---|
| 99 | // navigatie vervangen.
|
|---|
| 100 | document.addEventListener('input', function (e) {
|
|---|
| 101 | if (e.target && e.target.id === 'msg-q') apply();
|
|---|
| 102 | });
|
|---|
| 103 |
|
|---|
| 104 | document.body.addEventListener('htmx:afterSettle', function (e) {
|
|---|
| 105 | // "Meer laden" voegt rijen toe aan de bestaande lijst; een navigatie brengt
|
|---|
| 106 | // een hele nieuwe lijst. In beide gevallen opnieuw indexeren en de actieve
|
|---|
| 107 | // chip toepassen.
|
|---|
| 108 | if (e.target && (e.target.id === 'msg-list' || e.target.querySelector && e.target.querySelector('.msg-list'))) {
|
|---|
| 109 | reindex();
|
|---|
| 110 | apply();
|
|---|
| 111 | }
|
|---|
| 112 | });
|
|---|
| 113 |
|
|---|
| 114 | // De bookmarklet-knop: bouwt zijn href pas in de browser, want hij heeft de
|
|---|
| 115 | // origin nodig.
|
|---|
| 116 | function wireBookmarklet() {
|
|---|
| 117 | var a = document.getElementById('fedi-bm-btn');
|
|---|
| 118 | if (!a || a.__wired) return;
|
|---|
| 119 | a.__wired = true;
|
|---|
| 120 | a.setAttribute('href', "javascript:void(window.open('" + location.origin + "/authorize_interaction?uri='+encodeURIComponent(window.location.href)))");
|
|---|
| 121 | a.addEventListener('click', function (e) {
|
|---|
| 122 | e.preventDefault();
|
|---|
| 123 | a.classList.add('nudge');
|
|---|
| 124 | setTimeout(function () { a.classList.remove('nudge'); }, 600);
|
|---|
| 125 | });
|
|---|
| 126 | }
|
|---|
| 127 |
|
|---|
| 128 | function start() { reindex(); apply(); wireBookmarklet(); }
|
|---|
| 129 | start();
|
|---|
| 130 | // Kom je hier via een link binnen de site, dan is de lijst er pas na de swap.
|
|---|
| 131 | document.body.addEventListener('pcmsNav', start);
|
|---|
| 132 | })();
|
|---|