openvibers/openvibe.vip Commits
The shared update system and one account UI, on the OpenVibe Frame (openvibe-shared 1.11.0, openvibe/frame): the home shows what shipped, /updates is the shared log and the footer links it; the shared navbar handles sign in and out (logoutUrl ends this site's session) and carries this site's account links in its menu, while the page's own account bar is only for no-JS visitors
Changes
@@@ -16,7 +16,7 @@"jsonwebtoken": "^9.0.2","openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.33.0","openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.5.0",-- "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.5.1"++ "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.11.0"},"engines": {"node": ">=22.0.0"@@@ -1008,9 +1008,9 @@}},"node_modules/openvibe-shared": {-- "version": "1.5.1",-- "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.5.1",-- "integrity": "sha512-1XWRFwN8br3iiy2Z8fmnXQMgR76VlfMpHjP1xr5uKLCJmtvtNSWO1BgA1kQmVtyBY7g+1n6YFIjahaD4qWbD/Q==",++ "version": "1.11.0",++ "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.11.0",++ "integrity": "sha512-edMW0I25HNFReLcfZ9ak+4q5sy2t7qq7EPG52tOerTBV2fU994C5we/02AD/9i2Vuq0+ayNBBisW0cg60L6DLQ==","license": "MIT","dependencies": {"jsonwebtoken": "^9.0.0"@@@ -28,7 +28,7 @@"jsonwebtoken": "^9.0.2","openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.33.0","openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.5.0",-- "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.5.1"++ "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.11.0"},"engines": {"node": ">=22.0.0"@@@ -2,7 +2,7 @@/*** Page shell for every server-rendered page: full <head> SEO (title, description, canonical,-- * robots, Open Graph), the shared OpenVibe chrome (inline critical canvas + app icon from++ * robots, Open Graph), the shared OpenVibe Frame (inline critical canvas + app icon from* openvibe-shared, navbar.js from the Network, the SSR footer and a <noscript> navigation), this* site's stylesheet and its small progressive script. Everything is useful without JavaScript.*/@@@ -10,7 +10,7 @@ const crypto = require('crypto');const fs = require('fs');const path = require('path');const appIcon = require('openvibe-shared/app-icon');--const chrome = require('openvibe-shared/chrome-ssr');++const frame = require('openvibe-shared/frame');const SITE_NAME = 'OpenVibe.VIP';const NETWORK_URL = 'https://openvibe.network';@@@ -54,7 +54,9 @@ function createLayout({ config, release }) {silentLogin: `${config.baseUrl}/auth/login?silent=1&next={url}`,sessionUrl: '/auth/me',loginUrl: `/auth/login?next=${encodeURIComponent(o.canonicalPath || '/')}`,++ logoutUrl: '/auth/logout?next={path}', // Sign out in the shared navbar ends this site's session too};++ const footer = { service: 'vip', variant: 'full', mount: '#ov-footer', brandName: SITE_NAME, updates: '/updates' };const jsonLd = (o.jsonLd || []).map((x) => `<script type="application/ld+json">${JSON.stringify(x).replace(/</g, '\\u003c')}</script>`).join('\n');const who = o.viewer? `<span class="who">Signed in as <b>${esc(o.viewer.name || o.viewer.username || 'you')}</b> · <a href="/auth/logout?next=/">Sign out</a></span>`@@@ -82,20 +84,23 @@ ${release ? release.metaTag() : ''}<link rel="stylesheet" href="${asset('css/vip.css')}">${jsonLd}<script src="${NETWORK_URL}/shared/navbar.js" defer></script>++<script src="${NETWORK_URL}/shared/footer.js" defer></script><script src="${asset('js/vip.js')}" defer></script></head><body><div id="navbar-mount"></div>--${chrome.noscriptNav({ name: SITE_NAME, home: '/', links: NAV_LINKS })}++${frame.noscriptNav({ name: SITE_NAME, home: '/', links: NAV_LINKS })}<header class="site-head"><a class="brand" href="/">${SITE_NAME}</a><nav>${NAV_LINKS.map((l) => `<a href="${l.href}"${o.active === l.label.toLowerCase() ? ' aria-current="page"' : ''}>${l.label}</a>`).join('')}</nav>${who}</header><main id="main" class="page">${o.body || ''}++${o.canonicalPath === '/' && o.active === 'home' ? frame.shipped({ service: 'vip', title: `Recently shipped on ${SITE_NAME}` }) : ''}</main>--${chrome.footer({ service: 'vip', variant: 'full' })}++${frame.footer(footer)}<script>--window.__OV_PAGE = ${JSON.stringify({ navbar: nav }).replace(/</g, '\\u003c')};++window.__OV_PAGE = ${JSON.stringify({ navbar: nav, footer }).replace(/</g, '\\u003c')};document.addEventListener('DOMContentLoaded', function () {try { if (window.OpenVibeNavbar) { OpenVibeNavbar.init(window.__OV_PAGE.navbar); document.documentElement.classList.add('ov-has-navbar'); } } catch (e) { /* the SSR header stays */ }++ try { if (window.OpenVibeFooter) OpenVibeFooter.init(window.__OV_PAGE.footer); } catch (e) { /* */ }});</script></body>@@@ -14,6 +14,7 @@* domain calls as the API and redirects back with a notice (?ok= / ?error=).*/const crypto = require('crypto');++const frame = require('openvibe-shared/frame');const express = require('express');const { VipError, bool } = require('../util');const { viewerMiddleware } = require('./session');@@@ -77,6 +78,8 @@ function createWebRoutes({ domain, config, layout, userAuth }) {res.type('application/xml').send(`<?xml version="1.0" encoding="UTF-8"?>\n<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">\n${urls.map((u) => `<url><loc>${u.replace(/&/g, '&')}</loc></url>`).join('\n')}\n</urlset>\n`);});++ // What shipped on OpenVibe.VIP: the shared update log every OpenVibe site has.++ router.get('/updates', (req, res) => send(res, 200, { viewer: req.viewer, canonicalPath: '/updates', title: 'What shipped on OpenVibe.VIP', body: frame.updatesBody({ service: 'vip', siteName: 'OpenVibe.VIP' }) + frame.shippedScript() }));router.get('/', (req, res) => send(res, 200, {active: 'home', viewer: req.viewer, canonicalPath: '/',body: pages.home({ creators: creators.listPublic().map(creators.present) }),
@@ -16,7 +16,7 @@
@@ -16,7 +16,7 @@
"jsonwebtoken": "^9.0.2",
"jsonwebtoken": "^9.0.2",
"openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.33.0",
"openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.33.0",
"openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.5.0",
"openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.5.0",
- "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.5.1"
+ "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.11.0"
},
},
"engines": {
"engines": {
"node": ">=22.0.0"
"node": ">=22.0.0"
@@ -1008,9 +1008,9 @@
@@ -1008,9 +1008,9 @@
}
}
},
},
"node_modules/openvibe-shared": {
"node_modules/openvibe-shared": {
- "version": "1.5.1",
- "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.5.1",
- "integrity": "sha512-1XWRFwN8br3iiy2Z8fmnXQMgR76VlfMpHjP1xr5uKLCJmtvtNSWO1BgA1kQmVtyBY7g+1n6YFIjahaD4qWbD/Q==",
+ "version": "1.11.0",
+ "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.11.0",
+ "integrity": "sha512-edMW0I25HNFReLcfZ9ak+4q5sy2t7qq7EPG52tOerTBV2fU994C5we/02AD/9i2Vuq0+ayNBBisW0cg60L6DLQ==",
"license": "MIT",
"license": "MIT",
"dependencies": {
"dependencies": {
"jsonwebtoken": "^9.0.0"
"jsonwebtoken": "^9.0.0"
@@ -28,7 +28,7 @@
@@ -28,7 +28,7 @@
"jsonwebtoken": "^9.0.2",
"jsonwebtoken": "^9.0.2",
"openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.33.0",
"openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.33.0",
"openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.5.0",
"openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.5.0",
- "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.5.1"
+ "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.11.0"
},
},
"engines": {
"engines": {
"node": ">=22.0.0"
"node": ">=22.0.0"
@@ -2,7 +2,7 @@
@@ -2,7 +2,7 @@
/**
/**
* Page shell for every server-rendered page: full <head> SEO (title, description, canonical,
* Page shell for every server-rendered page: full <head> SEO (title, description, canonical,
- * robots, Open Graph), the shared OpenVibe chrome (inline critical canvas + app icon from
+ * robots, Open Graph), the shared OpenVibe Frame (inline critical canvas + app icon from
* openvibe-shared, navbar.js from the Network, the SSR footer and a <noscript> navigation), this
* openvibe-shared, navbar.js from the Network, the SSR footer and a <noscript> navigation), this
* site's stylesheet and its small progressive script. Everything is useful without JavaScript.
* site's stylesheet and its small progressive script. Everything is useful without JavaScript.
*/
*/
@@ -10,7 +10,7 @@ const crypto = require('crypto');
@@ -10,7 +10,7 @@ const crypto = require('crypto');
const fs = require('fs');
const fs = require('fs');
const path = require('path');
const path = require('path');
const appIcon = require('openvibe-shared/app-icon');
const appIcon = require('openvibe-shared/app-icon');
-const chrome = require('openvibe-shared/chrome-ssr');
+const frame = require('openvibe-shared/frame');
const SITE_NAME = 'OpenVibe.VIP';
const SITE_NAME = 'OpenVibe.VIP';
const NETWORK_URL = 'https://openvibe.network';
const NETWORK_URL = 'https://openvibe.network';
@@ -54,7 +54,9 @@ function createLayout({ config, release }) {
@@ -54,7 +54,9 @@ function createLayout({ config, release }) {
silentLogin: `${config.baseUrl}/auth/login?silent=1&next={url}`,
silentLogin: `${config.baseUrl}/auth/login?silent=1&next={url}`,
sessionUrl: '/auth/me',
sessionUrl: '/auth/me',
loginUrl: `/auth/login?next=${encodeURIComponent(o.canonicalPath || '/')}`,
loginUrl: `/auth/login?next=${encodeURIComponent(o.canonicalPath || '/')}`,
+ logoutUrl: '/auth/logout?next={path}', // Sign out in the shared navbar ends this site's session too
};
};
+ const footer = { service: 'vip', variant: 'full', mount: '#ov-footer', brandName: SITE_NAME, updates: '/updates' };
const jsonLd = (o.jsonLd || []).map((x) => `<script type="application/ld+json">${JSON.stringify(x).replace(/</g, '\\u003c')}</script>`).join('\n');
const jsonLd = (o.jsonLd || []).map((x) => `<script type="application/ld+json">${JSON.stringify(x).replace(/</g, '\\u003c')}</script>`).join('\n');
const who = o.viewer
const who = o.viewer
? `<span class="who">Signed in as <b>${esc(o.viewer.name || o.viewer.username || 'you')}</b> · <a href="/auth/logout?next=/">Sign out</a></span>`
? `<span class="who">Signed in as <b>${esc(o.viewer.name || o.viewer.username || 'you')}</b> · <a href="/auth/logout?next=/">Sign out</a></span>`
@@ -82,20 +84,23 @@ ${release ? release.metaTag() : ''}
@@ -82,20 +84,23 @@ ${release ? release.metaTag() : ''}
<link rel="stylesheet" href="${asset('css/vip.css')}">
<link rel="stylesheet" href="${asset('css/vip.css')}">
${jsonLd}
${jsonLd}
<script src="${NETWORK_URL}/shared/navbar.js" defer></script>
<script src="${NETWORK_URL}/shared/navbar.js" defer></script>
+<script src="${NETWORK_URL}/shared/footer.js" defer></script>
<script src="${asset('js/vip.js')}" defer></script>
<script src="${asset('js/vip.js')}" defer></script>
</head>
</head>
<body>
<body>
<div id="navbar-mount"></div>
<div id="navbar-mount"></div>
-${chrome.noscriptNav({ name: SITE_NAME, home: '/', links: NAV_LINKS })}
+${frame.noscriptNav({ name: SITE_NAME, home: '/', links: NAV_LINKS })}
<header class="site-head"><a class="brand" href="/">${SITE_NAME}</a><nav>${NAV_LINKS.map((l) => `<a href="${l.href}"${o.active === l.label.toLowerCase() ? ' aria-current="page"' : ''}>${l.label}</a>`).join('')}</nav>${who}</header>
<header class="site-head"><a class="brand" href="/">${SITE_NAME}</a><nav>${NAV_LINKS.map((l) => `<a href="${l.href}"${o.active === l.label.toLowerCase() ? ' aria-current="page"' : ''}>${l.label}</a>`).join('')}</nav>${who}</header>
<main id="main" class="page">
<main id="main" class="page">
${o.body || ''}
${o.body || ''}
+${o.canonicalPath === '/' && o.active === 'home' ? frame.shipped({ service: 'vip', title: `Recently shipped on ${SITE_NAME}` }) : ''}
</main>
</main>
-${chrome.footer({ service: 'vip', variant: 'full' })}
+${frame.footer(footer)}
<script>
<script>
-window.__OV_PAGE = ${JSON.stringify({ navbar: nav }).replace(/</g, '\\u003c')};
+window.__OV_PAGE = ${JSON.stringify({ navbar: nav, footer }).replace(/</g, '\\u003c')};
document.addEventListener('DOMContentLoaded', function () {
document.addEventListener('DOMContentLoaded', function () {
try { if (window.OpenVibeNavbar) { OpenVibeNavbar.init(window.__OV_PAGE.navbar); document.documentElement.classList.add('ov-has-navbar'); } } catch (e) { /* the SSR header stays */ }
try { if (window.OpenVibeNavbar) { OpenVibeNavbar.init(window.__OV_PAGE.navbar); document.documentElement.classList.add('ov-has-navbar'); } } catch (e) { /* the SSR header stays */ }
+ try { if (window.OpenVibeFooter) OpenVibeFooter.init(window.__OV_PAGE.footer); } catch (e) { /* */ }
});
});
</script>
</script>
</body>
</body>
@@ -14,6 +14,7 @@
@@ -14,6 +14,7 @@
* domain calls as the API and redirects back with a notice (?ok= / ?error=).
* domain calls as the API and redirects back with a notice (?ok= / ?error=).
*/
*/
const crypto = require('crypto');
const crypto = require('crypto');
+const frame = require('openvibe-shared/frame');
const express = require('express');
const express = require('express');
const { VipError, bool } = require('../util');
const { VipError, bool } = require('../util');
const { viewerMiddleware } = require('./session');
const { viewerMiddleware } = require('./session');
@@ -77,6 +78,8 @@ function createWebRoutes({ domain, config, layout, userAuth }) {
@@ -77,6 +78,8 @@ function createWebRoutes({ domain, config, layout, userAuth }) {
res.type('application/xml').send(`<?xml version="1.0" encoding="UTF-8"?>\n<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">\n${urls.map((u) => `<url><loc>${u.replace(/&/g, '&')}</loc></url>`).join('\n')}\n</urlset>\n`);
res.type('application/xml').send(`<?xml version="1.0" encoding="UTF-8"?>\n<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">\n${urls.map((u) => `<url><loc>${u.replace(/&/g, '&')}</loc></url>`).join('\n')}\n</urlset>\n`);
});
});
+ // What shipped on OpenVibe.VIP: the shared update log every OpenVibe site has.
+ router.get('/updates', (req, res) => send(res, 200, { viewer: req.viewer, canonicalPath: '/updates', title: 'What shipped on OpenVibe.VIP', body: frame.updatesBody({ service: 'vip', siteName: 'OpenVibe.VIP' }) + frame.shippedScript() }));
router.get('/', (req, res) => send(res, 200, {
router.get('/', (req, res) => send(res, 200, {
active: 'home', viewer: req.viewer, canonicalPath: '/',
active: 'home', viewer: req.viewer, canonicalPath: '/',
body: pages.home({ creators: creators.listPublic().map(creators.present) }),
body: pages.home({ creators: creators.listPublic().map(creators.present) }),