openvibers/openvibe.quest Commits

Quest pages describe the quest (why, where, what it counts, the badge) instead of the why line alone (#67)

OP OpenVibers committed 24 days ago · afff4875ed · 2 files, +16 −1

Changes

ddiff --git a/server/http/pages.js b/server/http/pages.js
iindex e952d6a..fa95314 100644
---- a/server/http/pages.js
++++ b/server/http/pages.js
@@@ -26,6 +26,17 @@ const TAGLINE = 'Quests across the whole network.';
const SUBJECT_RE = /^usr_[0-9A-HJKMNP-TV-Z]{26}$/;
const siteName = (domain) => (catalog.SITES[domain] || {}).name || domain;
++/**
++ * A quest page's meta description (≤ 160 characters): why it matters, where it happens, what it counts and its badge.
++ * It was the "why" line alone ("An answer is worth more than a read."), which says nothing about the quest.
++ */
++function questDescription(quest) {
++ const steps = quest.steps.map((st) => (st.count > 1 ? `${st.label} ×${st.count}` : st.label)).join(', ');
++ const badge = quest.reward && quest.reward.badge && quest.reward.badge.name ? ` Badge: ${quest.reward.badge.name}.` : '';
++ const text = `${String(quest.why || '').trim().replace(/[.!?]*$/, '.')} A quest on ${siteName(quest.site)}: ${steps}.${badge}`;
++ return text.length <= 160 ? text : `${text.slice(0, 159).replace(/\s+\S*$/, '')}…`;
++}
++
function createPageRoutes(ctx) {
const { config, s, coins } = ctx;
const r = asyncRouter();
@@@ -153,7 +164,7 @@ ${completion ? html`<div class="notice" role="status">You completed this quest o
return load().then((state) => page(req, res, {
index: true, cache: mine ? null : PUBLIC_CACHE,
title: quest.title,
-- description: quest.why,
++ description: questDescription(quest),
crumbs: [{ label: 'Quests', href: '/' }, { label: quest.title }],
body: html`<h1>${quest.title}</h1>
<p class="lede">${quest.why}</p>
ddiff --git a/test/items.test.js b/test/items.test.js
iindex 90febfb..81bd6a6 100644
---- a/test/items.test.js
++++ b/test/items.test.js
@@@ -118,6 +118,10 @@ function stubInventory() {
try {
const page = await t.get('/quests/say-hello-in-chat');
assert.ok(page.text.includes('class="reward-item"') && page.text.includes('Sparkle'), 'the reward chip');
++ // The page describes the quest (why, where, what it counts, the badge), not its "why" line alone.
++ const desc = ((page.text.match(/<meta name="description" content="([^"]*)">/) || [])[1] || '').replace(/&#39;/g, "'");
++ assert.match(desc, /\. A quest on OpenVibe\.\w+: .+\.( Badge: .+\.)?$/, desc);
++ assert.ok(desc.length <= 160, desc);
const api = (await t.get('/api/v1/quests')).json();
const q = (api.quests || api).find((x) => x.id === 'say-hello-in-chat');
assert.deepStrictEqual(q.reward.item, { issuer: 'service:live', alias: 'px_sparkle', name: 'Sparkle', kind: 'Particle effect' });