openvibers/openvibe.meme Commits

Describe Meme entry pages for search and share cards (#55)

OP OpenVibers committed 12 days ago · 439ab5370a · 2 files, +28 −2

Changes

ddiff --git a/server/http/pages.js b/server/http/pages.js
iindex b57512a..a4ac96c 100644
---- a/server/http/pages.js
++++ b/server/http/pages.js
@@@ -56,6 +56,19 @@ function createPageRoutes(ctx) {
});
// ── One entry ────────────────────────────────────────────
++ /** A search result and share-card line: the entry's own summary or first use, cut at a word to 155 characters. */
++ function metaDescription(e, citations = []) {
++ const origin = citations.find((c) => c.role === 'origin') || citations[0] || null;
++ const own = [e.summary, origin && origin.quote_text]
++ .map((v) => (typeof v === 'string' ? v.replace(/\s+/g, ' ').trim() : ''))
++ .find((v) => v.length >= 40);
++ if (!own) return `${e.title}: where the joke started, how it spread, and every use on OpenVibe.`;
++ if (own.length <= 155) return own;
++ const cut = own.slice(0, 154);
++ const at = cut.lastIndexOf(' ');
++ return `${(at > 40 ? cut.slice(0, at) : cut).replace(/[\s,;:.!?-]+$/, '')}…`;
++ }
++
async function entryPage(req, res, { problem = null } = {}) {
const slug = String(req.params.slug || '');
const row = await entries.bySlug(slug);
@@@ -63,17 +76,18 @@ function createPageRoutes(ctx) {
if (!row || (row.visibility === 'hidden' && !staff)) return notFound(req, res);
const citations = await entries.citations(row.id);
const e = entries.wireEntry(row);
++ const description = metaDescription(e, citations);
const jsonLd = seo.jsonLd.webPage({
name: e.title,
url: `${base()}/m/${e.slug}`,
-- description: (e.summary || `${e.title} on OpenVibe.Meme`).slice(0, 160),
++ description,
siteUrl: base(),
});
return page(req, res, {
index: e.visibility === 'public',
cache: e.visibility === 'public' ? PUBLIC_CACHE : null,
title: e.title,
-- description: (e.summary || `${e.title} on OpenVibe.Meme`).slice(0, 160),
++ description,
jsonLd: [jsonLd],
body: raw(views.entry({ e, citations, signedIn: Boolean(signedIn(req)), staff, reported: req.query.reported === '1', problem })),
});
ddiff --git a/test/pages.test.js b/test/pages.test.js
iindex de7f4be..5e5203d 100644
---- a/test/pages.test.js
++++ b/test/pages.test.js
@@@ -26,6 +26,11 @@ function jsonLd(html) {
{ role: 'use', source_service: 'live', source_type: 'vod', source_id: '9002', vod_id: 9002, offset_seconds: 120, quote_text: 'the robot is offline for good', url: `${t.upstreams.url}/v/9002?t=120`, observed_at: now },
],
});
++ const describedEntry = await t.ctx.entries.createCandidate({
++ slug: 'cat-typing', title: 'Cat typing', kind: 'meme', status: 'rising',
++ summary: 'A cat at a keyboard, typed as though the cat is answering a work email one slow paw at a time.',
++ first_seen_at: now, last_seen_at: now, visibility: 'public', citations: [],
++ });
const hiddenEntry = await t.ctx.entries.createCandidate({
slug: 'not-yet', title: 'Not yet', kind: 'joke', summary: 'Hidden.', status: 'rising',
first_seen_at: now, last_seen_at: now, visibility: 'hidden',
@@@ -65,6 +70,13 @@ function jsonLd(html) {
assert.ok(r.text.includes('/v/9002?t=120'));
});
++ await check('one entry page describes itself for search and share cards', async () => {
++ const fallback = await t.get('/m/robot-is-offline');
++ assert.ok(fallback.text.includes('<meta name="description" content="Robot is offline: where the joke started, how it spread, and every use on OpenVibe.">'), 'the fallback line');
++ const own = await t.get('/m/cat-typing');
++ assert.ok(own.text.includes('<meta name="description" content="A cat at a keyboard, typed as though the cat is answering a work email one slow paw at a time.">'), 'the entry\'s own summary');
++ });
++
await check('a hidden entry is 404 to the public and visible to staff', async () => {
assert.strictEqual((await t.get('/m/not-yet')).status, 404);
assert.strictEqual((await t.get('/m/not-yet', { as: alice })).status, 404);