openvibers/openvibe.food Commits
Share: one Share sheet on every recipe (#218)
Changes
@@@ -12,7 +12,8 @@* Every page says the recipe was written by OpenVibe.AI; an unreviewed one is noindex.*/const showcase = require('openvibe-shared/showcase');--const { html, notice, time } = require('../render/html');++const kit = require('openvibe-shared/kit');++const { html, raw, notice, time } = require('../render/html');const { sameOrigin } = require('./principal');const { COST_NOTE } = require('../food/recipes');@@@ -79,11 +80,12 @@ ${pages > 1 ? html`<nav class="pager" aria-label="Pages">${pageNo > 1 ? html`<aauthor: { '@type': 'Organization', name: 'OpenVibe.Food' }, datePublished: x.created_at,}] : [];page(req, res, {-- title: x.title, index: Boolean(x.reviewed && x.status === 'published'), localStyles: ['css/recipes.css'], jsonLd,++ title: x.title, index: Boolean(x.reviewed && x.status === 'published'), localStyles: ['css/recipes.css'], styles: [showcase.STYLESHEET, 'kit.css'], jsonLd,description: `${x.title}: ${meta(x)}. A budget recipe from OpenVibe.Food's food list, written by AI.`,crumbs: [{ label: 'Recipe library', href: '/recipes/library' }, { label: x.title }],body: html`<article class="recipe lib-recipe"><header class="recipe-head"><h1>${x.title}</h1><p class="recipe-meta">${meta(x)}</p></header>++<p>${raw(kit.share({ title: x.title, url, text: `${x.title}: ${meta(x)}. A budget recipe from OpenVibe.Food's food list, written by AI.` }))}</p><p class="notice">${AI_LINE}${x.reviewed ? ' A person at OpenVibe has checked this one.' : ''}</p><div class="recipe-body"><section class="recipe-ingredients" aria-label="Ingredients"><h2 class="h4">Ingredients</h2>nnew file mode 100644@@@ -0,0 +1,48 @@++'use strict';++/**++ * Every recipe in the library carries the network's one Share button (openvibe-shared 4.114.0): one++ * [data-ov-share-sheet] in its head, with the recipe's canonical address, title and line. A recipe has no picture of++ * its own, so there is no data-image. The navbar loads the sheet on the first press.++ */++const assert = require('assert');++const { boot } = require('./helpers/boot');++const { buildSources } = require('../server/food/recipes');++++const src = buildSources();++const at = (slug) => src.findIndex((x) => x.source_id === slug);++const ai = {++ async run() {++ return {++ runId: 'run_share_1',++ output: {++ recipes: [{++ title: 'Beans and Rice', serves: 4, minutes: 25,++ ingredients: [++ { name: 'Rice', amount: '2 cups', from_pantry: false, citations: [at('white-rice')] },++ { name: 'Beans', amount: '1 can', from_pantry: false, citations: [at('canned-beans')] },++ ],++ steps: ['Cook the rice.', 'Warm the beans.', 'Serve together.'], notes: 'Add hot sauce.',++ }],++ citations: [], gaps: [],++ },++ };++ },++};++++(async () => {++ const t = await boot({ env: { OV_AI_INTERNAL_URL: 'http://ai.test' }, ai });++ try {++ const [recipe] = await t.ctx.library.tick({ force: true });++ const r = await t.get(`/recipes/library/${recipe.slug}`);++ assert.strictEqual(r.status, 200, r.text.slice(0, 200));++ assert.strictEqual((r.text.match(/data-ov-share-sheet/g) || []).length, 1, 'one Share button');++ assert.ok(r.text.includes(`data-url="https://openvibe.food/recipes/library/${recipe.slug}"`), 'the recipe address');++ assert.ok(r.text.includes('data-title="Beans and Rice"'), 'its title');++ assert.ok(r.text.includes('data-text="Beans and Rice: Serves 4 · About 25 min'), 'its line');++ assert.ok(r.text.includes('class="ovk-btn ovk-btn--quiet"') && r.text.includes('<span>Share</span>'), 'the shared kit button');++ assert.ok(!r.text.includes('data-image='), 'no picture: no data-image');++ console.log('food share sheet ok');++ } finally {++ await t.close();++ }++})().catch((e) => { console.error(e); process.exit(1); });
@@ -12,7 +12,8 @@
@@ -12,7 +12,8 @@
* Every page says the recipe was written by OpenVibe.AI; an unreviewed one is noindex.
* Every page says the recipe was written by OpenVibe.AI; an unreviewed one is noindex.
*/
*/
const showcase = require('openvibe-shared/showcase');
const showcase = require('openvibe-shared/showcase');
-const { html, notice, time } = require('../render/html');
+const kit = require('openvibe-shared/kit');
+const { html, raw, notice, time } = require('../render/html');
const { sameOrigin } = require('./principal');
const { sameOrigin } = require('./principal');
const { COST_NOTE } = require('../food/recipes');
const { COST_NOTE } = require('../food/recipes');
@@ -79,11 +80,12 @@ ${pages > 1 ? html`<nav class="pager" aria-label="Pages">${pageNo > 1 ? html`<a
@@ -79,11 +80,12 @@ ${pages > 1 ? html`<nav class="pager" aria-label="Pages">${pageNo > 1 ? html`<a
author: { '@type': 'Organization', name: 'OpenVibe.Food' }, datePublished: x.created_at,
author: { '@type': 'Organization', name: 'OpenVibe.Food' }, datePublished: x.created_at,
}] : [];
}] : [];
page(req, res, {
page(req, res, {
- title: x.title, index: Boolean(x.reviewed && x.status === 'published'), localStyles: ['css/recipes.css'], jsonLd,
+ title: x.title, index: Boolean(x.reviewed && x.status === 'published'), localStyles: ['css/recipes.css'], styles: [showcase.STYLESHEET, 'kit.css'], jsonLd,
description: `${x.title}: ${meta(x)}. A budget recipe from OpenVibe.Food's food list, written by AI.`,
description: `${x.title}: ${meta(x)}. A budget recipe from OpenVibe.Food's food list, written by AI.`,
crumbs: [{ label: 'Recipe library', href: '/recipes/library' }, { label: x.title }],
crumbs: [{ label: 'Recipe library', href: '/recipes/library' }, { label: x.title }],
body: html`<article class="recipe lib-recipe">
body: html`<article class="recipe lib-recipe">
<header class="recipe-head"><h1>${x.title}</h1><p class="recipe-meta">${meta(x)}</p></header>
<header class="recipe-head"><h1>${x.title}</h1><p class="recipe-meta">${meta(x)}</p></header>
+<p>${raw(kit.share({ title: x.title, url, text: `${x.title}: ${meta(x)}. A budget recipe from OpenVibe.Food's food list, written by AI.` }))}</p>
<p class="notice">${AI_LINE}${x.reviewed ? ' A person at OpenVibe has checked this one.' : ''}</p>
<p class="notice">${AI_LINE}${x.reviewed ? ' A person at OpenVibe has checked this one.' : ''}</p>
<div class="recipe-body">
<div class="recipe-body">
<section class="recipe-ingredients" aria-label="Ingredients"><h2 class="h4">Ingredients</h2>
<section class="recipe-ingredients" aria-label="Ingredients"><h2 class="h4">Ingredients</h2>
new file mode 100644
new file mode 100644
@@ -0,0 +1,48 @@
@@ -0,0 +1,48 @@
+'use strict';
+/**
+ * Every recipe in the library carries the network's one Share button (openvibe-shared 4.114.0): one
+ * [data-ov-share-sheet] in its head, with the recipe's canonical address, title and line. A recipe has no picture of
+ * its own, so there is no data-image. The navbar loads the sheet on the first press.
+ */
+const assert = require('assert');
+const { boot } = require('./helpers/boot');
+const { buildSources } = require('../server/food/recipes');
+
+const src = buildSources();
+const at = (slug) => src.findIndex((x) => x.source_id === slug);
+const ai = {
+ async run() {
+ return {
+ runId: 'run_share_1',
+ output: {
+ recipes: [{
+ title: 'Beans and Rice', serves: 4, minutes: 25,
+ ingredients: [
+ { name: 'Rice', amount: '2 cups', from_pantry: false, citations: [at('white-rice')] },
+ { name: 'Beans', amount: '1 can', from_pantry: false, citations: [at('canned-beans')] },
+ ],
+ steps: ['Cook the rice.', 'Warm the beans.', 'Serve together.'], notes: 'Add hot sauce.',
+ }],
+ citations: [], gaps: [],
+ },
+ };
+ },
+};
+
+(async () => {
+ const t = await boot({ env: { OV_AI_INTERNAL_URL: 'http://ai.test' }, ai });
+ try {
+ const [recipe] = await t.ctx.library.tick({ force: true });
+ const r = await t.get(`/recipes/library/${recipe.slug}`);
+ assert.strictEqual(r.status, 200, r.text.slice(0, 200));
+ assert.strictEqual((r.text.match(/data-ov-share-sheet/g) || []).length, 1, 'one Share button');
+ assert.ok(r.text.includes(`data-url="https://openvibe.food/recipes/library/${recipe.slug}"`), 'the recipe address');
+ assert.ok(r.text.includes('data-title="Beans and Rice"'), 'its title');
+ assert.ok(r.text.includes('data-text="Beans and Rice: Serves 4 · About 25 min'), 'its line');
+ assert.ok(r.text.includes('class="ovk-btn ovk-btn--quiet"') && r.text.includes('<span>Share</span>'), 'the shared kit button');
+ assert.ok(!r.text.includes('data-image='), 'no picture: no data-image');
+ console.log('food share sheet ok');
+ } finally {
+ await t.close();
+ }
+})().catch((e) => { console.error(e); process.exit(1); });