openvibers/openvibe.shared Commits

Profile panel: a hidden [data-ov-profile-section] around a "me" panel shows with it, so a site can give your stuff a heading (#116)

OP OpenVibers committed 19 days ago · 6c860c58f5 · 3 files, +20 −10

Changes

ddiff --git a/README.md b/README.md
iindex 23b5df5..b72a853 100644
---- a/README.md
++++ b/README.md
@@@ -711,7 +711,8 @@ able to see your inventory anywhere"). Put a fallback link in an element and loa
Every `[data-ov-profile]` on the page is drawn once loaded; `OpenVibeProfilePanel.render(el, username)` draws one added
later. `data-ov-profile="me"` is whoever is signed in on that site (its own `/auth/me`, the navbar's session lookup), so a
--page cached for everyone can still show "your stuff": give the element `hidden` and it appears only once drawn. It reads OpenVibe.Network's public profile (`GET https://openvibe.network/api/v1/profiles/:username`, CORS
++page cached for everyone can still show "your stuff": give the element `hidden` and it appears only once drawn. A
++hidden ancestor marked `data-ov-profile-section` (a heading and a line around the panel) is shown with it. It reads OpenVibe.Network's public profile (`GET https://openvibe.network/api/v1/profiles/:username`, CORS
open, no credentials), which gathers OpenVibe.Inventory, OpenVibe.Quest and OpenVibe.Play. Values are text, links only
to OpenVibe's own https hosts, pictures only from openvibe.media and openvibe.network; a private profile shows its name
and picture; no answer leaves the fallback link. `window.OpenVibeProfilePanelConfig = { api }` points it at another
ddiff --git a/profile-panel.js b/profile-panel.js
iindex cc3189b..8fabd32 100644
---- a/profile-panel.js
++++ b/profile-panel.js
@@@ -8,7 +8,9 @@
* OpenVibeProfilePanel.render(el, 'ana') or every [data-ov-profile] on the page, by itself, once loaded
* <section data-ov-profile="me" hidden>…</section> whoever is signed in on this site (its own /auth/me, the navbar's
* session lookup), so a cached page can show "your stuff"; shown
-- * only once drawn, left as it was for someone signed out
++ * only once drawn, left as it was for someone signed out;
++ * a hidden [data-ov-profile-section] around it (a heading and
++ * a line) is shown with it
*
* It reads OpenVibe.Network's public profile (GET https://openvibe.network/api/v1/profiles/:username: profile@1, CORS
* open, no credentials, cached 60 s by the Network), which gathers Inventory, Quest and Play. The panel replaces the
@@@ -78,6 +80,13 @@
return box;
}
++ /** Show the drawn panel, and the section around it that waits for it ([data-ov-profile-section], hidden until then). */
++ function reveal(target) {
++ target.hidden = false;
++ const section = target.closest && target.closest('[data-ov-profile-section]');
++ if (section) section.hidden = false;
++ }
++
function draw(target, p) {
const card = el('section', 'ovpp');
card.setAttribute('aria-label', `${p.display_name || p.username} on OpenVibe`);
@@@ -94,7 +103,7 @@
if (p.private) {
card.appendChild(el('p', 'ovpp-note', 'This profile is private.'));
target.replaceChildren(card);
-- target.hidden = false;
++ reveal(target);
return card;
}
const ach = p.achievements || { total: 0, games: [] };
@@@ -147,7 +156,7 @@
if (p.inventory_url) { foot.appendChild(root.document.createTextNode(' \u00b7 ')); foot.appendChild(link(p.inventory_url, null, 'Inventory')); }
card.appendChild(foot);
target.replaceChildren(card);
-- target.hidden = false;
++ reveal(target);
return card;
}
ddiff --git a/test/profile-panel.test.js b/test/profile-panel.test.js
iindex d72e3be..ede14dc 100644
---- a/test/profile-panel.test.js
++++ b/test/profile-panel.test.js
@@@ -33,7 +33,7 @@ const PAGE = `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta n
<body style="margin:0;padding:16px"><div id="ana" data-ov-profile="ana"><a href="https://openvibe.network/@ana">Ana</a></div>
<div id="priv" data-ov-profile="hidden"><a href="https://openvibe.network/@hidden">Hidden</a></div>
<div id="none" data-ov-profile="nobody"><a href="https://openvibe.network/@nobody">Nobody</a></div>
--<section id="me" data-ov-profile="me" hidden><a href="https://openvibe.network/my">Your stuff</a></section>
++<section id="stuff" data-ov-profile-section hidden><h2>Your stuff</h2><div id="me" data-ov-profile="me" hidden><a href="https://openvibe.network/my">Your stuff</a></div></section>
<script>window.OpenVibeProfilePanelConfig = { api: location.origin };</script>
<script src="/shared/profile-panel.js" defer></script></body></html>`;
const server = http.createServer((req, res) => {
@@@ -43,7 +43,7 @@ const server = http.createServer((req, res) => {
if (u.pathname === '/api/v1/profiles/ana') return json(200, { profile: ANA });
// Signed in on the main page; signed out on /out (told apart by the page that asks).
if (u.pathname === '/auth/me') return json(200, { user: /\/out$/.test(req.headers.referer || '') ? null : { id: 7, username: 'ana' } });
-- if (u.pathname === '/out') { res.writeHead(200, { 'content-type': 'text/html; charset=utf-8' }); return res.end(PAGE.replace(/<div id="ana"[\s\S]*?<\/section>/, '<section id="me" data-ov-profile="me" hidden><a href="https://openvibe.network/my">Your stuff</a></section>')); }
++ if (u.pathname === '/out') { res.writeHead(200, { 'content-type': 'text/html; charset=utf-8' }); return res.end(PAGE.replace(/<div id="ana"[\s\S]*?<\/section>/, '<section id="stuff" data-ov-profile-section hidden><h2>Your stuff</h2><div id="me" data-ov-profile="me" hidden><a href="https://openvibe.network/my">Your stuff</a></div></section>')); }
if (u.pathname === '/api/v1/profiles/hidden') return json(200, { profile: { username: 'hidden', display_name: 'Hidden', profile_url: 'https://openvibe.network/@hidden', private: true } });
if (u.pathname.startsWith('/api/v1/profiles/')) return json(404, { error: 'not_found' });
if (u.pathname.startsWith('/avatar/')) { res.writeHead(204); return res.end(); }
@@@ -75,7 +75,7 @@ const server = http.createServer((req, res) => {
priv: document.querySelector('#priv .ovpp').textContent,
none: document.getElementById('none').innerHTML,
wide: document.documentElement.scrollWidth,
-- me: [document.getElementById('me').hidden, document.querySelector('#me .ovpp-who small').textContent],
++ me: [document.getElementById('me').hidden, document.querySelector('#me .ovpp-who small').textContent, document.getElementById('stuff').hidden],
}));
}, 100); } }, 30); })`));
assert.strictEqual(got.name, 'Ana <img src=x onerror=alert(1)>', 'a name with markup stays text');
@@@ -89,10 +89,10 @@ const server = http.createServer((req, res) => {
assert.match(got.priv, /Hidden[\s\S]*This profile is private\./);
assert.match(got.none, /href="https:\/\/openvibe\.network\/@nobody"/, 'no profile: the fallback link stays');
assert.ok(got.wide <= 360, `fits a 360 px phone (${got.wide})`);
-- assert.deepStrictEqual(got.me, [false, '@ana'], '"me" is whoever /auth/me names, shown once drawn');
++ assert.deepStrictEqual(got.me, [false, '@ana', false], '"me" is whoever /auth/me names, shown once drawn with the section around it');
await p.goto(`${base}/out`, { maxMs: 5000 });
-- const out = await p.evaluate(`new Promise((r) => setTimeout(() => r(JSON.stringify([document.getElementById('me').hidden, !!document.querySelector('#me .ovpp')])), 700))`);
-- assert.deepStrictEqual(JSON.parse(out), [true, false], 'signed out: "me" stays hidden and undrawn');
++ const out = await p.evaluate(`new Promise((r) => setTimeout(() => r(JSON.stringify([document.getElementById('me').hidden, !!document.querySelector('#me .ovpp'), document.getElementById('stuff').hidden])), 700))`);
++ assert.deepStrictEqual(JSON.parse(out), [true, false, true], 'signed out: "me" and its section stay hidden, undrawn');
console.log('profile-panel: ok');
} finally {
await browser.close();