openvibers/openvibe.events Commits

Public events: the shared explanation said once; each card says only its own (#202)

OP OpenVibers committed 11 days ago · b0bfc1028c · 3 files, +18 −10

Changes

ddiff --git a/server/home.js b/server/home.js
iindex 731d198..cf9590f 100644
---- a/server/home.js
++++ b/server/home.js
@@@ -98,7 +98,9 @@ function busOrbit(stats) {
}
const nsOf = (type) => String(type).split('.')[0];
const slug = (s) => String(s).toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '');
--const eventTitle = (type) => TYPE_WORDS[type] || `An event of type ${type}`;
++// A type with no words of its own reads as its publisher and what happened ("OpenVibe.Host: release published"), not
++// "An event of type host.release.published" over the same name again.
++const eventTitle = (type) => TYPE_WORDS[type] || `${serviceName(nsOf(type))}: ${String(type).split('.').slice(1).join(' ').replace(/_/g, ' ')}`;
const eventDescription = (type) => `Published by ${serviceName(nsOf(type))} as ${type}. Every subscriber receives it as a signed webhook; any reader can pull it with a cursor.`;
const isoDate = (ms) => { const n = Number(ms); return Number.isFinite(n) && n > 0 ? new Date(n).toISOString() : null; };
const humanDate = (ms) => {
@@@ -159,11 +161,12 @@ function eventCard(r, cat) {
const url = serviceUrl(ns);
const category = url ? `<a href="${esc(url)}">${esc(ns)}</a>` : esc(ns);
const when = iso ? `<time datetime="${iso}">${esc(humanDate(r.last))}</time>` : '<span class="hm-event-undated">date not recorded</span>';
++ // What every public event is (a signed webhook to each subscriber, a cursor for any reader) is said once, above the
++ // list; each card says only its own: what happened, when it last arrived, who published it, how often, its name.
++ void category;
return `<li class="hm-event" id="event-${esc(slug(type))}" data-cat="${esc(cat)}">
--<p class="hm-event-cat">Category: ${category}</p>
<h3 class="hm-event-title">${esc(eventTitle(type))}</h3>
--<p class="hm-event-meta">${when} · Online, from ${serviceLink(ns)} · ${esc(fmt(r.n))} this week</p>
--<p class="hm-event-desc">${esc(eventDescription(type))}</p>
++<p class="hm-event-meta">${when} · from ${serviceLink(ns)} · ${esc(fmt(r.n))} this week</p>
<p class="hm-event-type"><code>${esc(type)}</code></p>
</li>`;
}
@@@ -193,7 +196,7 @@ function eventsSection(stats) {
const firstOf = (ns) => list.find((r) => nsOf(r.type) === ns);
const chips = cats.map((ns) => `<a class="pick-chip" href="#event-${esc(slug(String(firstOf(ns).type)))}">${esc(serviceName(ns))}</a>`).join('');
const cards = list.map((r) => eventCard(r, String(cats.indexOf(nsOf(r.type))))).join('');
-- inner = `<p class="sc-lede">The public event types anyone may read, newest arrival first: when each last arrived, who published it and what it means. Subscribe to a pattern such as <code>live.stream.*</code> to get them as signed webhooks.</p>
++ inner = `<p class="sc-lede">The public event types anyone may read, newest arrival first: when each last arrived, who published it and what it means. Subscribe to a pattern such as <code>live.stream.*</code> to get them as signed webhooks; any reader can also pull them with a cursor.</p>
<div class="pick pick-events" id="pick-events"><nav class="pick-row" aria-label="Public events this week">${chips}</nav>
<ul class="hm-events">${cards}</ul></div>
${publicTypes.length > list.length ? `<p class="hm-note">And ${esc(fmt(publicTypes.length - list.length))} more public event types this week.</p>` : ''}`;
ddiff --git a/test/home-stats.test.js b/test/home-stats.test.js
iindex dbcb660..a79f1c7 100644
---- a/test/home-stats.test.js
++++ b/test/home-stats.test.js
@@@ -49,7 +49,7 @@ t('published events show as counts by service, public types and the latest publi
assert.match(fold(page, 'who'), /<a href="https:\/\/openvibe\.live">OpenVibe\.Live<\/a>/);
assert.match(events, /A channel went live on OpenVibe\.Live/);
assert.match(events, /<time datetime="\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z">/);
-- assert.match(events, /Online, from <a href="https:\/\/openvibe\.live">OpenVibe\.Live<\/a> · 2 this week/);
++ assert.match(events, / · from <a href="https:\/\/openvibe\.live">OpenVibe\.Live<\/a> · 2 this week/);
assert.match(events, /<code>live\.stream\.started<\/code>/);
assert.match(events, /A post was published on OpenVibe\.Blog/);
assert.match(events, /<code>blog\.post\.published<\/code>/);
ddiff --git a/test/home.test.js b/test/home.test.js
iindex c388495..fe8e442 100644
---- a/test/home.test.js
++++ b/test/home.test.js
@@@ -176,14 +176,19 @@ t('the server-rendered event list: title, real <time datetime>, place, descripti
const card = /<li class="hm-event"[\s\S]*?<\/li>/.exec(page)[0];
assert.match(card, /A channel went live on OpenVibe\.Live/, 'the event title');
assert.match(card, /<time datetime="2026-10-29T09:41:00\.000Z">/, 'a real <time datetime>');
-- assert.match(card, /Online, from <a href="https:\/\/openvibe\.live">OpenVibe\.Live<\/a>/, 'the place and publisher');
-- assert.match(card, /Published by OpenVibe\.Live as live\.stream\.started/, 'the short description');
++ assert.match(card, / · from <a href="https:\/\/openvibe\.live">OpenVibe\.Live<\/a> · /, 'the publisher');
assert.match(card, /3 this week/, 'the count');
-- assert.match(card, /Category: <a href="https:\/\/openvibe\.live">live<\/a>/, 'a category link');
++ // What every event is, said once above the list (2026-11-02: the same sentence on 24 cards made the page 9 screens).
++ assert.ok(!card.includes('Every subscriber receives it') && !card.includes('Category:'), 'no boilerplate on the card');
++ assert.match(page, /signed webhooks; any reader can also pull them with a cursor/, 'said once in the lede');
++ // A type without its own words reads as its publisher and what happened.
++ const odd = renderHome({ siteUrl: SITE, apiUrl: SITE, stats: { ...stats, public: [{ type: 'host.release_note.published', n: 1, last }] } });
++ assert.match(odd, /<h3 class="hm-event-title">OpenVibe\.Host: release note published<\/h3>/, 'its publisher and what happened');
++ assert.ok(!odd.includes('An event of type'), 'never "An event of type …"');
// The category nav became the row of chips that picks one category at a time on a phone (the short-pages rule);
// the links it held are still in the page, on the cards themselves.
assert.match(page, /<a class="pick-chip" href="#event-[a-z0-9-]+">OpenVibe\.Live<\/a>/, 'the category chips jump to their first card');
-- assert.match(page, /Online, from <a href="https:\/\/openvibe\.live">OpenVibe\.Live<\/a>/, 'the publisher link is still in the page');
++ assert.match(page, /from <a href="https:\/\/openvibe\.live">OpenVibe\.Live<\/a>/, 'the publisher link is still in the page');
assert.match(page, /<code>live\.stream\.started<\/code>/, 'the exact event type');
// One JSON-LD script per page: every node lives in the page's @graph.