openvibers/openvibe.meme Commits

The one Share sheet on a meme page (#88)

OP OpenVibers committed 7 days ago · c06ea16f9f · 3 files, +22 −6

* The one Share sheet on a meme page

Give the meme pages the shared sheet button (openvibe-shared 4.114.0): the entry page and a saved caption's page each carry one button, with the page's own public address.

- server/views/index.js, server/http/pages.js: the entry page's button carries the site's own share card as its picture; a caption's button carries the template picture it is written over
- test/pages.test.js: both pages are checked for one button with the right address

* One Share button everywhere: the shared kit's

The Share button is now the shared kit's (openvibe-shared 4.114.0 kit.share): the same quiet kit button with the same share icon on every OpenVibe site.

- server/views/index.js: the entry page's button sits under the title's meta line (beside the summary when there is room); the caption page's button sits in its own actions row

Changes

ddiff --git a/server/http/pages.js b/server/http/pages.js
iindex decd037..1a19f8b 100644
---- a/server/http/pages.js
++++ b/server/http/pages.js
@@@ -15,6 +15,7 @@
*/
const express = require('express');
const seo = require('openvibe-shared/seo');
++const social = require('openvibe-shared/social');
const { asyncRouter } = require('./router');
const { createDiscoveryRoutes, homeJsonLd } = require('./discovery');
const { sameOrigin } = require('./principal');
@@@ -90,7 +91,8 @@ function createPageRoutes(ctx) {
title: e.title,
description,
jsonLd: [jsonLd],
-- body: raw(views.entry({ e, citations, signedIn: Boolean(signedIn(req)), staff, reported: req.query.reported === '1', problem })),
++ body: raw(views.entry({ e, citations, signedIn: Boolean(signedIn(req)), staff, reported: req.query.reported === '1', problem,
++ share: { url: `${base()}/m/${e.slug}`, image: social.url('meme') } })),
});
}
r.get('/m/:slug', (req, res) => entryPage(req, res));
@@@ -177,7 +179,7 @@ function createPageRoutes(ctx) {
cache: c.visibility === 'public' ? PUBLIC_CACHE : null,
title: template ? `A caption on ${template.name}` : 'A saved caption',
description: c.lines.join(' / ').slice(0, 160),
-- body: raw(views.caption({ caption: c, template, mine })),
++ body: raw(views.caption({ caption: c, template, mine, share: { url: `${base()}/c/${c.id}` } })),
});
});
ddiff --git a/server/views/index.js b/server/views/index.js
iindex 3468fa9..72056c8 100644
---- a/server/views/index.js
++++ b/server/views/index.js
@@@ -26,6 +26,11 @@ const KIND_LABEL = { joke: 'Joke', catchphrase: 'Catchphrase', bit: 'Bit', meme:
const SOURCE_LABEL = { chat_message: 'Chat', vod: 'VOD', clip: 'Clip', rundown_segment: 'The Rundown', ai_paste: 'Community', stream_memory: 'Stream memory' };
const statusBadge = (s) => `<span class="mm-status mm-status-${esc(s)}">${esc(STATUS_LABEL[s] || s)}</span>`;
++
++/** The entry's Share button, built by the shared kit (openvibe-shared 4.114.0 kit.share): the same quiet button with
++ * the same share icon on every OpenVibe site. It sits right under the title's meta line, beside it when there is
++ * room, so on a phone it never stands alone on a line of its own. */
++const shareButton = (e, share) => kit.share({ title: e.title, url: share.url, text: e.summary || '', image: share.image || null });
const kindLabel = (k) => esc(KIND_LABEL[k] || k);
function clock(seconds) {
@@@ -124,7 +129,7 @@ function home({ trending = [], week = null, counts = {} } = {}) {
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, share = null } = {}) {
const origin = citations.find((c) => c.role === 'origin') || citations[0] || null;
const uses = citations.filter((c) => c !== origin);
const examples = citations.filter((c) => c.role === 'example');
@@@ -134,7 +139,7 @@ function entry({ e, citations = [], signedIn = false, staff = false, reported =
<header class="mm-entry-h">
<p class="mm-kicker">${kindLabel(e.kind)} · ${statusBadge(e.status)}${staff ? ' · <span class="mm-staff">staff view</span>' : ''}</p>
<h1>${esc(e.title)}</h1>
--${e.summary ? `<p class="mm-summary">${esc(e.summary)}</p>` : ''}
++${e.summary ? `<p class="mm-summary">${esc(e.summary)}${share && share.url ? ' ' + shareButton(e, share) : ''}</p>` : (share && share.url ? shareButton(e, share) : '')}
<dl class="mm-facts">
<div><dt>First seen</dt><dd><time datetime="${esc(e.first_seen_at)}">${esc(String(e.first_seen_at || '').slice(0, 10))}</time></dd></div>
<div><dt>Last seen</dt><dd><time datetime="${esc(e.last_seen_at)}">${esc(String(e.last_seen_at || '').slice(0, 10))}</time></dd></div>
@@@ -213,7 +218,7 @@ ${preview ? `<figure class="mm-caption-card"><img src="${esc(preview.template.im
</div>`;
}
--function caption({ caption: c, template, mine = false } = {}) {
++function caption({ caption: c, template, mine = false, share = null } = {}) {
return `<div class="mm-wrap" id="mm-caption" data-caption="${esc(c.id)}">
<header class="mm-page-h"><p class="mm-kicker">A saved caption</p><h1>${esc(template ? template.name : 'Caption')}</h1>
<p class="mm-lede">${mine ? 'Yours. ' : ''}Text over the template image, saved as lines — never as a rendered picture.</p></header>
@@@ -222,7 +227,7 @@ ${template ? `<img src="${esc(template.image_url)}" alt="">` : ''}
<figcaption>${c.lines.map((l) => `<span>${esc(l)}</span>`).join('')}</figcaption>
</figure>
<dl class="mm-facts"><div><dt>Saved</dt><dd>${time(c.created_at)}</dd></div><div><dt>Visibility</dt><dd>${esc(c.visibility)}</dd></div><div><dt>Caption</dt><dd><code>${esc(c.id)}</code></dd></div></dl>
--<div class="mm-actions"><a class="mm-btn" href="/make">Make another</a><a class="mm-btn mm-btn-quiet" href="/">Back to the memes</a></div>
++<div class="mm-actions">${share && share.url ? kit.share({ title: template ? template.name : 'Caption', url: share.url, image: (template && template.image_url) || null }) : ''}<a class="mm-btn" href="/make">Make another</a><a class="mm-btn mm-btn-quiet" href="/">Back to the memes</a></div>
</div>`;
}
ddiff --git a/test/pages.test.js b/test/pages.test.js
iindex b997453..eedb13f 100644
---- a/test/pages.test.js
++++ b/test/pages.test.js
@@@ -68,6 +68,11 @@ function jsonLd(html) {
assert.ok(r.text.includes('the robot is offline again'));
assert.ok(r.text.includes('the robot is offline for good'));
assert.ok(r.text.includes('/v/9002?t=120'));
++ // The one Share sheet (openvibe-shared 4.114.0): one button, with the entry's own public address.
++ const share = r.text.match(/<button[^>]*data-ov-share-sheet[^>]*>/g) || [];
++ assert.strictEqual(share.length, 1, 'one Share button: ' + share.join(' | '));
++ assert.ok(share[0].includes('data-url="https://openvibe.meme/m/robot-is-offline"'), share[0]);
++ assert.ok(share[0].includes('data-image="/shared/social/meme.jpg'), share[0]);
});
await check('one entry page describes itself for search and share cards', async () => {
@@@ -143,6 +148,10 @@ function jsonLd(html) {
assert.ok(r.text.includes('mm-caption-card'));
assert.ok(r.text.includes('the caption'));
assert.ok(r.text.includes('on the page'));
++ const share = r.text.match(/<button[^>]*data-ov-share-sheet[^>]*>/g) || [];
++ assert.strictEqual(share.length, 1, 'one Share button: ' + share.join(' | '));
++ assert.ok(share[0].includes(`data-url="https://openvibe.meme/c/${cap.id}"`), share[0]);
++ assert.ok(share[0].includes('data-image="/img/template-'), 'the template is the picture');
const hidden = (await t.get('/api/v1/captions', { as: alice, json: { template_id: tpl.id, lines: ['hidden one'], visibility: 'hidden' } })).json();
assert.strictEqual((await t.get(`/c/${hidden.id}`)).status, 404);
assert.strictEqual((await t.get(`/c/${hidden.id}`, { as: alice })).status, 200);