openvibers/openvibe.meme Commits
Meme's home leads with what is real; one 'be the first' row instead of empty sections (#32)
Changes
@@@ -158,3 +158,9 @@ html.ov-motion .mm-demo-bars i { animation: mm-bar .9s cubic-bezier(.2,.7,.2,1)@media (max-width: 860px) { .mm-make-grid { grid-template-columns: minmax(0, 1fr); } .mm-preview { position: static; order: -1; } }@media (prefers-reduced-motion: reduce) { .mm-tree-lines path, .mm-sticker, .mm-demo-bars i { animation: none !important; } }html[data-ov-motion=reduced] .mm-tree-lines path, html[data-ov-motion=reduced] .mm-sticker, html[data-ov-motion=reduced] .mm-demo-bars i { animation: none !important; }++++/* Be the first (10-31): what is still missing, one short row below the scenes, never an empty section up top. */++.mm-first-list { list-style: none; margin: 10px 0 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr)); gap: 10px; }++.mm-first-list li { display: grid; gap: 4px; padding: 14px 16px; border-radius: 16px; border: 1px dashed color-mix(in srgb, var(--room-accent) 45%, var(--border, #1f2d47)); }++.mm-first-list span { color: var(--text-secondary, #96a7c2); font-size: 14px; line-height: 1.45; }++.mm-first-list a { justify-self: start; margin-top: 4px; font-weight: 700; color: var(--room-strong, var(--room-accent)); }@@@ -73,21 +73,29 @@ function treeMotif() {function home({ trending = [], week = null, counts = {} } = {}) {const candidates = week ? week.candidates.slice(0, 3) : [];-- const live = `<section class="mm-section" id="mm-trending" aria-labelledby="mm-trending-h">++ // Real first (the owner, 10-31: empty sections made the rooms look unfinished). Trending and the week's teaser show only++ // when they have entries; while neither does, one short "be the first" row sits below the scenes instead.++ const trendingSec = trending.length ? `<section class="mm-section" id="mm-trending" aria-labelledby="mm-trending-h"><div class="mm-head-row"><h2 id="mm-trending-h">Trending</h2><span class="mm-count">${Number(counts.entries) || trending.length} public ${Number(counts.entries) === 1 ? 'entry' : 'entries'}</span></div>--${trending.length ? `<ol class="mm-entries">${trending.slice(0, 8).map(card).join('')}</ol>` : empty('No entry is public yet. The first proof is being checked against its source.', '/developers', 'How entries are built')}--</section>--<section class="mm-section" id="mm-week-teaser" aria-labelledby="mm-week-teaser-h">++<ol class="mm-entries">${trending.slice(0, 8).map(card).join('')}</ol>++</section>` : '';++ const weekSec = candidates.length ? `<section class="mm-section" id="mm-week-teaser" aria-labelledby="mm-week-teaser-h"><div class="mm-head-row"><h2 id="mm-week-teaser-h">Meme of the week</h2><a class="mm-more" href="/week">The ballot${kit.icon('chevron')}</a></div>--${candidates.length ? `<ol class="mm-entries mm-entries-week">${candidates.map(card).join('')}</ol>` : empty('No candidate has a use this week yet. The ballot opens as soon as one does.')}--</section>`;++<ol class="mm-entries mm-entries-week">${candidates.map(card).join('')}</ol>++</section>` : '';++ const firsts = [++ !trending.length && ['The first entries are on their way', 'Each one is checked against the chat line or VOD second it started from before it is public.', '/developers', 'How entries are built'],++ !candidates.length && ['Make the first caption', 'Pick a template, write the lines, and it is saved as text anyone can remix.', '/make', 'Open the generator'],++ ].filter(Boolean);++ const beFirst = firsts.length ? `<section class="mm-section mm-first" aria-labelledby="mm-first-h"><h2 id="mm-first-h">Be the first</h2><ul class="mm-first-list">${firsts.map(([title, text, href, label]) => `<li><b>${esc(title)}</b><span>${esc(text)}</span><a href="${esc(href)}">${esc(label)}</a></li>`).join('')}</ul></section>` : '';++ const live = `${trendingSec}${weekSec}`;const room = sc.room({room: 'meme',hero: {title: 'Every joke has a first time',lede: 'The open meme database. Each running joke is written down with the moment it happened, a chat line, a VOD second, a Rundown segment, and every later use. People vote for meme of the week, and anyone can caption a template.',start: { href: '/week', label: 'Vote for meme of the week' },-- chips: [{ label: 'Make a caption', href: '/make' }, { label: 'Trending', href: '#mm-trending' }, { label: 'The API', href: '/developers' }],++ chips: [{ label: 'Make a caption', href: '/make' }, ...(trending.length ? [{ label: 'Trending', href: '#mm-trending' }] : []), { label: 'The API', href: '/developers' }],proof: Number(counts.entries) ? [{ value: Number(counts.entries), label: Number(counts.entries) === 1 ? 'entry' : 'entries' }] : [],motif: treeMotif(),},@@@ -108,8 +116,11 @@ ${candidates.length ? `<ol class="mm-entries mm-entries-week">${candidates.map(c['Is the database open?', 'Yes: every public entry and citation is in the API, and in OpenVibe\'s Search.'],] },});++ // What is real goes above the scenes; what is still missing goes after them.const at = room.indexOf('<section class="sc-room-scenes"');-- return `<div class="mm-wrap mm-home" id="mm-home">${at < 0 ? room : room.slice(0, at) + live + room.slice(at)}</div>`;++ const end = at < 0 ? -1 : room.indexOf('</section>', at) + '</section>'.length;++ const page = at < 0 ? live + room + beFirst : room.slice(0, at) + live + room.slice(at, end) + beFirst + room.slice(end);++ return `<div class="mm-wrap mm-home" id="mm-home">${page}</div>`;}function entry({ e, citations = [], signedIn = false, staff = false, reported = false, problem = null } = {}) {nnew file mode 100644@@@ -0,0 +1,24 @@++'use strict';++// Meme's home leads with what is real (the owner, 10-31: empty sections made the rooms look unfinished). With no public++// entry and no candidate this week, there is no empty Trending or Meme of the week up top: one short "be the first" row++// sits after the scenes and says how each starts. Once there are entries, they lead above the scenes.++const assert = require('assert');++const fs = require('fs');++const path = require('path');++const views = require('../server/views');++++const src = fs.readFileSync(path.join(__dirname, '..', 'server/views/index.js'), 'utf8');++assert.match(src, /room\.slice\(0, at\) \+ live \+ room\.slice\(at, end\) \+ beFirst \+ room\.slice\(end\)/, 'the real above the scenes, the missing after them');++++const empty = views.home({ trending: [], week: null, counts: {} });++assert.ok(!empty.includes('id="mm-trending"') && !empty.includes('id="mm-week-teaser"'), 'no empty section');++assert.ok(!empty.includes('href="#mm-trending"'), 'no chip to a section that is not there');++const scenes = empty.indexOf('class="sc-room-scenes"');++assert.ok(scenes > 0 && empty.indexOf('Be the first') > scenes, 'the row follows the scenes');++assert.ok(empty.includes('href="/make">Open the generator</a>') && empty.includes('href="/developers">How entries are built</a>'), 'each says how it starts');++++const entry = { slug: 'robot-offline', title: 'Robot is offline', kind: 'joke', summary: 'When the robot drops.', status: 'rising', uses: 4, citations: [] };++const some = views.home({ trending: [entry], week: { candidates: [entry] }, counts: { entries: 1 } });++assert.ok(some.indexOf('id="mm-trending"') > 0 && some.indexOf('id="mm-trending"') < some.indexOf('class="sc-room-scenes"'), 'entries lead above the scenes');++assert.ok(!some.includes('Be the first'), 'nothing missing, no row');++console.log('meme home: real first, one "be the first" row after the scenes while nothing is public');
@@ -158,3 +158,9 @@ html.ov-motion .mm-demo-bars i { animation: mm-bar .9s cubic-bezier(.2,.7,.2,1)
@@ -158,3 +158,9 @@ html.ov-motion .mm-demo-bars i { animation: mm-bar .9s cubic-bezier(.2,.7,.2,1)
@media (max-width: 860px) { .mm-make-grid { grid-template-columns: minmax(0, 1fr); } .mm-preview { position: static; order: -1; } }
@media (max-width: 860px) { .mm-make-grid { grid-template-columns: minmax(0, 1fr); } .mm-preview { position: static; order: -1; } }
@media (prefers-reduced-motion: reduce) { .mm-tree-lines path, .mm-sticker, .mm-demo-bars i { animation: none !important; } }
@media (prefers-reduced-motion: reduce) { .mm-tree-lines path, .mm-sticker, .mm-demo-bars i { animation: none !important; } }
html[data-ov-motion=reduced] .mm-tree-lines path, html[data-ov-motion=reduced] .mm-sticker, html[data-ov-motion=reduced] .mm-demo-bars i { animation: none !important; }
html[data-ov-motion=reduced] .mm-tree-lines path, html[data-ov-motion=reduced] .mm-sticker, html[data-ov-motion=reduced] .mm-demo-bars i { animation: none !important; }
+
+/* Be the first (10-31): what is still missing, one short row below the scenes, never an empty section up top. */
+.mm-first-list { list-style: none; margin: 10px 0 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr)); gap: 10px; }
+.mm-first-list li { display: grid; gap: 4px; padding: 14px 16px; border-radius: 16px; border: 1px dashed color-mix(in srgb, var(--room-accent) 45%, var(--border, #1f2d47)); }
+.mm-first-list span { color: var(--text-secondary, #96a7c2); font-size: 14px; line-height: 1.45; }
+.mm-first-list a { justify-self: start; margin-top: 4px; font-weight: 700; color: var(--room-strong, var(--room-accent)); }
@@ -73,21 +73,29 @@ function treeMotif() {
@@ -73,21 +73,29 @@ function treeMotif() {
function home({ trending = [], week = null, counts = {} } = {}) {
function home({ trending = [], week = null, counts = {} } = {}) {
const candidates = week ? week.candidates.slice(0, 3) : [];
const candidates = week ? week.candidates.slice(0, 3) : [];
- const live = `<section class="mm-section" id="mm-trending" aria-labelledby="mm-trending-h">
+ // Real first (the owner, 10-31: empty sections made the rooms look unfinished). Trending and the week's teaser show only
+ // when they have entries; while neither does, one short "be the first" row sits below the scenes instead.
+ const trendingSec = trending.length ? `<section class="mm-section" id="mm-trending" aria-labelledby="mm-trending-h">
<div class="mm-head-row"><h2 id="mm-trending-h">Trending</h2><span class="mm-count">${Number(counts.entries) || trending.length} public ${Number(counts.entries) === 1 ? 'entry' : 'entries'}</span></div>
<div class="mm-head-row"><h2 id="mm-trending-h">Trending</h2><span class="mm-count">${Number(counts.entries) || trending.length} public ${Number(counts.entries) === 1 ? 'entry' : 'entries'}</span></div>
-${trending.length ? `<ol class="mm-entries">${trending.slice(0, 8).map(card).join('')}</ol>` : empty('No entry is public yet. The first proof is being checked against its source.', '/developers', 'How entries are built')}
-</section>
-<section class="mm-section" id="mm-week-teaser" aria-labelledby="mm-week-teaser-h">
+<ol class="mm-entries">${trending.slice(0, 8).map(card).join('')}</ol>
+</section>` : '';
+ const weekSec = candidates.length ? `<section class="mm-section" id="mm-week-teaser" aria-labelledby="mm-week-teaser-h">
<div class="mm-head-row"><h2 id="mm-week-teaser-h">Meme of the week</h2><a class="mm-more" href="/week">The ballot${kit.icon('chevron')}</a></div>
<div class="mm-head-row"><h2 id="mm-week-teaser-h">Meme of the week</h2><a class="mm-more" href="/week">The ballot${kit.icon('chevron')}</a></div>
-${candidates.length ? `<ol class="mm-entries mm-entries-week">${candidates.map(card).join('')}</ol>` : empty('No candidate has a use this week yet. The ballot opens as soon as one does.')}
-</section>`;
+<ol class="mm-entries mm-entries-week">${candidates.map(card).join('')}</ol>
+</section>` : '';
+ const firsts = [
+ !trending.length && ['The first entries are on their way', 'Each one is checked against the chat line or VOD second it started from before it is public.', '/developers', 'How entries are built'],
+ !candidates.length && ['Make the first caption', 'Pick a template, write the lines, and it is saved as text anyone can remix.', '/make', 'Open the generator'],
+ ].filter(Boolean);
+ const beFirst = firsts.length ? `<section class="mm-section mm-first" aria-labelledby="mm-first-h"><h2 id="mm-first-h">Be the first</h2><ul class="mm-first-list">${firsts.map(([title, text, href, label]) => `<li><b>${esc(title)}</b><span>${esc(text)}</span><a href="${esc(href)}">${esc(label)}</a></li>`).join('')}</ul></section>` : '';
+ const live = `${trendingSec}${weekSec}`;
const room = sc.room({
const room = sc.room({
room: 'meme',
room: 'meme',
hero: {
hero: {
title: 'Every joke has a first time',
title: 'Every joke has a first time',
lede: 'The open meme database. Each running joke is written down with the moment it happened, a chat line, a VOD second, a Rundown segment, and every later use. People vote for meme of the week, and anyone can caption a template.',
lede: 'The open meme database. Each running joke is written down with the moment it happened, a chat line, a VOD second, a Rundown segment, and every later use. People vote for meme of the week, and anyone can caption a template.',
start: { href: '/week', label: 'Vote for meme of the week' },
start: { href: '/week', label: 'Vote for meme of the week' },
- chips: [{ label: 'Make a caption', href: '/make' }, { label: 'Trending', href: '#mm-trending' }, { label: 'The API', href: '/developers' }],
+ chips: [{ label: 'Make a caption', href: '/make' }, ...(trending.length ? [{ label: 'Trending', href: '#mm-trending' }] : []), { label: 'The API', href: '/developers' }],
proof: Number(counts.entries) ? [{ value: Number(counts.entries), label: Number(counts.entries) === 1 ? 'entry' : 'entries' }] : [],
proof: Number(counts.entries) ? [{ value: Number(counts.entries), label: Number(counts.entries) === 1 ? 'entry' : 'entries' }] : [],
motif: treeMotif(),
motif: treeMotif(),
},
},
@@ -108,8 +116,11 @@ ${candidates.length ? `<ol class="mm-entries mm-entries-week">${candidates.map(c
@@ -108,8 +116,11 @@ ${candidates.length ? `<ol class="mm-entries mm-entries-week">${candidates.map(c
['Is the database open?', 'Yes: every public entry and citation is in the API, and in OpenVibe\'s Search.'],
['Is the database open?', 'Yes: every public entry and citation is in the API, and in OpenVibe\'s Search.'],
] },
] },
});
});
+ // What is real goes above the scenes; what is still missing goes after them.
const at = room.indexOf('<section class="sc-room-scenes"');
const at = room.indexOf('<section class="sc-room-scenes"');
- return `<div class="mm-wrap mm-home" id="mm-home">${at < 0 ? room : room.slice(0, at) + live + room.slice(at)}</div>`;
+ const end = at < 0 ? -1 : room.indexOf('</section>', at) + '</section>'.length;
+ const page = at < 0 ? live + room + beFirst : room.slice(0, at) + live + room.slice(at, end) + beFirst + room.slice(end);
+ return `<div class="mm-wrap mm-home" id="mm-home">${page}</div>`;
}
}
function entry({ e, citations = [], signedIn = false, staff = false, reported = false, problem = null } = {}) {
function entry({ e, citations = [], signedIn = false, staff = false, reported = false, problem = null } = {}) {
new file mode 100644
new file mode 100644
@@ -0,0 +1,24 @@
@@ -0,0 +1,24 @@
+'use strict';
+// Meme's home leads with what is real (the owner, 10-31: empty sections made the rooms look unfinished). With no public
+// entry and no candidate this week, there is no empty Trending or Meme of the week up top: one short "be the first" row
+// sits after the scenes and says how each starts. Once there are entries, they lead above the scenes.
+const assert = require('assert');
+const fs = require('fs');
+const path = require('path');
+const views = require('../server/views');
+
+const src = fs.readFileSync(path.join(__dirname, '..', 'server/views/index.js'), 'utf8');
+assert.match(src, /room\.slice\(0, at\) \+ live \+ room\.slice\(at, end\) \+ beFirst \+ room\.slice\(end\)/, 'the real above the scenes, the missing after them');
+
+const empty = views.home({ trending: [], week: null, counts: {} });
+assert.ok(!empty.includes('id="mm-trending"') && !empty.includes('id="mm-week-teaser"'), 'no empty section');
+assert.ok(!empty.includes('href="#mm-trending"'), 'no chip to a section that is not there');
+const scenes = empty.indexOf('class="sc-room-scenes"');
+assert.ok(scenes > 0 && empty.indexOf('Be the first') > scenes, 'the row follows the scenes');
+assert.ok(empty.includes('href="/make">Open the generator</a>') && empty.includes('href="/developers">How entries are built</a>'), 'each says how it starts');
+
+const entry = { slug: 'robot-offline', title: 'Robot is offline', kind: 'joke', summary: 'When the robot drops.', status: 'rising', uses: 4, citations: [] };
+const some = views.home({ trending: [entry], week: { candidates: [entry] }, counts: { entries: 1 } });
+assert.ok(some.indexOf('id="mm-trending"') > 0 && some.indexOf('id="mm-trending"') < some.indexOf('class="sc-room-scenes"'), 'entries lead above the scenes');
+assert.ok(!some.includes('Be the first'), 'nothing missing, no row');
+console.log('meme home: real first, one "be the first" row after the scenes while nothing is public');