openvibers/openvibe.network Commits
Remove pronouns from public pages and the profile API (#700)
Changes
@@@ -216,7 +216,6 @@ function renderProfile(user) {const pageBio = document.getElementById('public-page-bio');if (pageBio) {const unavailable = !!user.is_anon;-- document.getElementById('public-page-pronouns').value = user.public_page_pronouns || '';pageBio.value = user.public_page_bio || '';let links = []; try { links = typeof user.public_page_links === 'string' ? JSON.parse(user.public_page_links) : (user.public_page_links || []); } catch {}document.getElementById('public-page-links').value = (Array.isArray(links) ? links : []).map(x => `${x.label} | ${x.url}`).join('\n');@@@ -224,7 +223,7 @@ function renderProfile(user) {for (const key of ['live', 'media', 'space', 'community']) document.getElementById(`public-place-${key}`).checked = places[key] !== false;const pageUrl = document.getElementById('public-page-url');if (user.username && !user.is_anon) { pageUrl.href = `https://openvibe.network/@${encodeURIComponent(user.username)}`; pageUrl.textContent = `openvibe.network/@${user.username}`; }-- for (const id of ['public-page-pronouns', 'public-page-bio', 'public-page-links', 'public-place-live', 'public-place-media', 'public-place-space', 'public-place-community', 'public-page-preview-toggle', 'public-page-save']) document.getElementById(id).disabled = unavailable;++ for (const id of ['public-page-bio', 'public-page-links', 'public-place-live', 'public-place-media', 'public-place-space', 'public-place-community', 'public-page-preview-toggle', 'public-page-save']) document.getElementById(id).disabled = unavailable;if (unavailable) document.getElementById('public-page-msg').textContent = 'Sign in to edit your public page.';}if (!user.is_anon) {@@@ -330,8 +329,6 @@ document.getElementById('profile-form').addEventListener('submit', async (e) =>const box = document.getElementById('public-page-preview'); box.hidden = false;box.replaceChildren();const h = document.createElement('h4'); h.textContent = currentUser?.display_name || currentUser?.username || 'Your name'; box.append(h);-- const pronouns = document.getElementById('public-page-pronouns').value;-- if (pronouns) { const byline = document.createElement('p'); byline.textContent = pronouns; box.append(byline); }const bio = document.createElement('p'); bio.textContent = document.getElementById('public-page-bio').value; box.append(bio);for (const row of document.getElementById('public-page-links').value.split('\n').filter(Boolean)) { const a = document.createElement('p'); a.textContent = row; box.append(a); }};@@@ -343,7 +340,7 @@ document.getElementById('profile-form').addEventListener('submit', async (e) =>const places = Object.fromEntries(['live', 'media', 'space', 'community'].map(key => [key, document.getElementById(`public-place-${key}`).checked]));save.disabled = true; msg.textContent = 'Saving…';try {-- const data = await apiFetch('/api/auth/profile', { method: 'PUT', body: JSON.stringify({ public_page_bio: document.getElementById('public-page-bio').value, public_page_pronouns: document.getElementById('public-page-pronouns').value, public_page_links: links, public_page_places: places }) });++ const data = await apiFetch('/api/auth/profile', { method: 'PUT', body: JSON.stringify({ public_page_bio: document.getElementById('public-page-bio').value, public_page_links: links, public_page_places: places }) });currentUser = data.user; msg.textContent = 'Your public page is saved.'; preview();} catch (err) { msg.textContent = err.message || 'Could not save your public page.'; }save.disabled = false;@@@ -225,10 +225,6 @@<h3><span class="icon"><i class="fa-solid fa-address-card"></i></span> Your public page</h3><p class="muted">A link-in-bio page at <a id="public-page-url" href="https://openvibe.network/" target="_blank" rel="noopener">openvibe.network/@you</a>. Only information already public while signed out can appear here.</p><!-- The same fields as Edit Profile above (.form-field): they were bare labels and boxes in a line (11-09). -->-- <div class="form-field">-- <label for="public-page-pronouns">Pronouns</label><input id="public-page-pronouns" maxlength="40" autocomplete="off">-- <div class="hint">Up to 40 characters.</div>-- </div><div class="form-field"><label for="public-page-bio">Bio</label><textarea id="public-page-bio" maxlength="280" rows="3"></textarea><div class="hint">Up to 280 characters.</div>@@@ -408,7 +408,7 @@ router.get('/me', requireAuth, async (req, res) => {// ── Update Profile ───────────────────────────────────────────router.put('/profile', requireAuth, async (req, res) => {const db = await getDb(req);-- const { display_name, bio, avatar_url, email, profile_color, profile_public, public_page_bio, public_page_pronouns, public_page_links, public_page_places } = req.body;++ const { display_name, bio, avatar_url, email, profile_color, profile_public, public_page_bio, public_page_links, public_page_places } = req.body;const updates = [];const params = [];@@@ -444,11 +444,6 @@ router.put('/profile', requireAuth, async (req, res) => {if (text.length > 280) return res.status(400).json({ error: 'Your public page bio can be up to 280 characters' });updates.push('public_page_bio = ?'); params.push(text);}-- if (public_page_pronouns !== undefined) {-- const text = String(public_page_pronouns || '').trim();-- if (text.length > 40) return res.status(400).json({ error: 'Pronouns can be up to 40 characters' });-- updates.push('public_page_pronouns = ?'); params.push(text);-- }if (public_page_links !== undefined) {if (!Array.isArray(public_page_links) || public_page_links.length > 8) return res.status(400).json({ error: 'Add up to 8 links' });const links = [];@@@ -470,8 +465,8 @@ router.put('/profile', requireAuth, async (req, res) => {if (updates.length === 0) return res.status(400).json({ error: 'No fields to update' });-- const publicPageTouched = public_page_bio !== undefined || public_page_pronouns !== undefined || public_page_links !== undefined || public_page_places !== undefined || profile_public !== undefined;-- const beforePage = publicPageTouched ? await db.prepare('SELECT username, profile_public, public_page_bio, public_page_pronouns, public_page_links, public_page_places FROM users WHERE id = ?').get(req.user.id) : null;++ const publicPageTouched = public_page_bio !== undefined || public_page_links !== undefined || public_page_places !== undefined || profile_public !== undefined;++ const beforePage = publicPageTouched ? await db.prepare('SELECT username, profile_public, public_page_bio, public_page_links, public_page_places FROM users WHERE id = ?').get(req.user.id) : null;updates.push('updated_at = ov_now()');params.push(req.user.id);@@@ -488,7 +483,6 @@ router.put('/profile', requireAuth, async (req, res) => {if (beforePage && publicPageTouched) {const json = (value) => { try { return typeof value === 'string' ? JSON.parse(value) : value; } catch { return null; } };const changed = String(beforePage.public_page_bio || '') !== String(user.public_page_bio || '')-- || String(beforePage.public_page_pronouns || '') !== String(user.public_page_pronouns || '')|| JSON.stringify(json(beforePage.public_page_links) || []) !== JSON.stringify(json(user.public_page_links) || [])|| JSON.stringify(json(beforePage.public_page_places) || {}) !== JSON.stringify(json(user.public_page_places) || {})|| Number(beforePage.profile_public) !== Number(user.profile_public);@@@ -284,7 +284,7 @@ function createPublicProfiles({ db, selfToken = () => null, inventoryUrl = 'httpconst rec = await usernames.lookup(db, name);if (!rec) return null;if (rec.renamed) return { moved: rec.current };-- const u = await db.prepare(`SELECT id, username, display_name, bio, public_page_bio, public_page_pronouns, public_page_links, public_page_places, profile_color, created_at, subject_id, profile_public,++ const u = await db.prepare(`SELECT id, username, display_name, bio, public_page_bio, public_page_links, public_page_places, profile_color, created_at, subject_id, profile_public,is_anon, merged_into, deleted_at FROM users WHERE id = ?`).get(rec.network_id);if (!u || Number(u.is_anon) || u.merged_into || u.deleted_at) return null;return { user: u };@@@ -295,7 +295,7 @@ function createPublicProfiles({ db, selfToken = () => null, inventoryUrl = 'httptry { links = typeof u.public_page_links === 'string' ? JSON.parse(u.public_page_links) : (u.public_page_links || []); } catch {}try { choices = { ...choices, ...(typeof u.public_page_places === 'string' ? JSON.parse(u.public_page_places) : (u.public_page_places || {})) }; } catch {}const places = Number(u.profile_public) ? await placesOf(u.username, choices) : { spaces: [], community: [], videos: [] };-- return { username: u.username, display_name: u.display_name || u.username, pronouns: Number(u.profile_public) ? String(u.public_page_pronouns || '').slice(0, 40) : '', avatar_url: avatarUrl(u.username), profile_url: `${SITE}/@${encodeURIComponent(u.username)}`, bio: Number(u.profile_public) ? String(u.public_page_bio || '').slice(0, 280) : '', links: Number(u.profile_public) && Array.isArray(links) ? links.slice(0, 8) : [], places };++ return { username: u.username, display_name: u.display_name || u.username, avatar_url: avatarUrl(u.username), profile_url: `${SITE}/@${encodeURIComponent(u.username)}`, bio: Number(u.profile_public) ? String(u.public_page_bio || '').slice(0, 280) : '', links: Number(u.profile_public) && Array.isArray(links) ? links.slice(0, 8) : [], places };}/** profile@1: what every site may show about a person. */@@@ -340,7 +340,7 @@ function createPublicProfiles({ db, selfToken = () => null, inventoryUrl = 'httpres.set('Cache-Control', cache.htmlHeaders({ maxAge: 60 }));// A person's public profile lives on openvibe.network (the owner, 11-08: "OpenVibe.My is for users' stuff,// the profiles should be on .Network"). openvibe.my is the person's own hub: /@name there moves here.-- res.send(renderPage(u, inv, SITE, page.places, page.pronouns));++ res.send(renderPage(u, inv, SITE, page.places));} catch (err) { next(err); }}@@@ -453,7 +453,7 @@ ${sorted.map((g) => {<p class="pf-note"><a href="${esc(all)}">${inv.more ? 'Every item' : 'This inventory'} on OpenVibe.Inventory</a> · <a href="${INVENTORY_SITE}/items">Every item there is</a></p></section>`;}--function renderPage(u, inv, origin = SITE, places = { spaces: [], community: [], videos: [] }, pronouns = '') {++function renderPage(u, inv, origin = SITE, places = { spaces: [], community: [], videos: [] }) {const canonicalUrl = `${origin}/@${encodeURIComponent(u.username)}`;const name = u.display_name || u.username;const isPublic = !!inv;@@@ -524,7 +524,7 @@ a{color:var(--accent)}.pf-who{flex:1;min-width:200px;padding-top:56px}.pf-who h1{margin:0;font-size:clamp(1.5rem,4vw,2.1rem);line-height:1.15;overflow-wrap:anywhere}.pf-name-badge{width:.85em;height:.85em;margin-right:.25em;vertical-align:-.08em;border-radius:6px;object-fit:contain}--.pf-handle{margin:.2em 0 0;color:var(--text-secondary)}.pf-pronouns{display:inline-block;margin-left:6px;padding:1px 9px;border:1px solid var(--border);border-radius:999px;font-size:.82em;vertical-align:1px}.pf-places a .pf-live{display:inline-flex;align-items:center;gap:6px;color:var(--live-red,#e5484d);font-weight:700}.pf-live::before{content:'';width:8px;height:8px;border-radius:50%;background:currentColor}++.pf-handle{margin:.2em 0 0;color:var(--text-secondary)}.pf-places a .pf-live{display:inline-flex;align-items:center;gap:6px;color:var(--live-red,#e5484d);font-weight:700}.pf-live::before{content:'';width:8px;height:8px;border-radius:50%;background:currentColor}.pf-bio{margin:0;padding:0 24px 18px;color:var(--text-primary);line-height:1.6;white-space:pre-line;overflow-wrap:anywhere;max-width:72ch}.pf-stats{list-style:none;display:flex;flex-wrap:wrap;gap:8px;margin:0;padding:0 24px 22px}.pf-stats li{display:flex;flex-direction:column;gap:2px;padding:8px 14px;border:1px solid var(--border);border-radius:12px;background:var(--bg-card);min-width:110px}@@@ -578,7 +578,7 @@ ${require('openvibe-shared/frame').noscriptNav({ name: 'OpenVibe.Network', links<div class="pf-banner" aria-hidden="true"></div><div class="pf-head"><div class="pf-pic"><img class="pf-avatar" src="${esc(avatarUrl(u.username))}" alt="" width="112" height="112">${hatGlyph ? `<span class="pf-hat" title="${esc(hat.name)}" aria-hidden="true">${esc(hatGlyph)}</span>` : ''}</div>--<div class="pf-who"><h1>${badgeId ? `<img class="pf-name-badge" src="https://openvibe.media/o/${badgeId}" alt="" title="${esc(badge.name)}" width="32" height="32">` : ''}${fxClass ? `<span class="ov-fx ${fxClass}" title="${esc(fx.name)}">${esc(name)}</span>` : esc(name)}</h1><p class="pf-handle">@${esc(u.username)}${isPublic && pronouns ? ` <span class="pf-pronouns">${esc(pronouns)}</span>` : ''}</p></div>++<div class="pf-who"><h1>${badgeId ? `<img class="pf-name-badge" src="https://openvibe.media/o/${badgeId}" alt="" title="${esc(badge.name)}" width="32" height="32">` : ''}${fxClass ? `<span class="ov-fx ${fxClass}" title="${esc(fx.name)}">${esc(name)}</span>` : esc(name)}</h1><p class="pf-handle">@${esc(u.username)}</p></div></div>${bio ? `<p class="pf-bio">${esc(bio)}</p>` : ''}${isPublic && Array.isArray(publicLinks) && publicLinks.length ? `<nav aria-label="${esc(name)}'s links"><ul class="pf-links">${publicLinks.slice(0, 8).map((link) => { let href = ''; try { const parsed = new URL(link.url); if (parsed.protocol === 'https:' && !parsed.username && !parsed.password) href = parsed.href; } catch {} return href ? `<li><a rel="me noopener noreferrer" href="${esc(href)}">${esc(String(link.label || '').slice(0, 40))}</a></li>` : ''; }).join('')}</ul></nav>` : ''}
@@ -216,7 +216,6 @@ function renderProfile(user) {
@@ -216,7 +216,6 @@ function renderProfile(user) {
const pageBio = document.getElementById('public-page-bio');
const pageBio = document.getElementById('public-page-bio');
if (pageBio) {
if (pageBio) {
const unavailable = !!user.is_anon;
const unavailable = !!user.is_anon;
- document.getElementById('public-page-pronouns').value = user.public_page_pronouns || '';
pageBio.value = user.public_page_bio || '';
pageBio.value = user.public_page_bio || '';
let links = []; try { links = typeof user.public_page_links === 'string' ? JSON.parse(user.public_page_links) : (user.public_page_links || []); } catch {}
let links = []; try { links = typeof user.public_page_links === 'string' ? JSON.parse(user.public_page_links) : (user.public_page_links || []); } catch {}
document.getElementById('public-page-links').value = (Array.isArray(links) ? links : []).map(x => `${x.label} | ${x.url}`).join('\n');
document.getElementById('public-page-links').value = (Array.isArray(links) ? links : []).map(x => `${x.label} | ${x.url}`).join('\n');
@@ -224,7 +223,7 @@ function renderProfile(user) {
@@ -224,7 +223,7 @@ function renderProfile(user) {
for (const key of ['live', 'media', 'space', 'community']) document.getElementById(`public-place-${key}`).checked = places[key] !== false;
for (const key of ['live', 'media', 'space', 'community']) document.getElementById(`public-place-${key}`).checked = places[key] !== false;
const pageUrl = document.getElementById('public-page-url');
const pageUrl = document.getElementById('public-page-url');
if (user.username && !user.is_anon) { pageUrl.href = `https://openvibe.network/@${encodeURIComponent(user.username)}`; pageUrl.textContent = `openvibe.network/@${user.username}`; }
if (user.username && !user.is_anon) { pageUrl.href = `https://openvibe.network/@${encodeURIComponent(user.username)}`; pageUrl.textContent = `openvibe.network/@${user.username}`; }
- for (const id of ['public-page-pronouns', 'public-page-bio', 'public-page-links', 'public-place-live', 'public-place-media', 'public-place-space', 'public-place-community', 'public-page-preview-toggle', 'public-page-save']) document.getElementById(id).disabled = unavailable;
+ for (const id of ['public-page-bio', 'public-page-links', 'public-place-live', 'public-place-media', 'public-place-space', 'public-place-community', 'public-page-preview-toggle', 'public-page-save']) document.getElementById(id).disabled = unavailable;
if (unavailable) document.getElementById('public-page-msg').textContent = 'Sign in to edit your public page.';
if (unavailable) document.getElementById('public-page-msg').textContent = 'Sign in to edit your public page.';
}
}
if (!user.is_anon) {
if (!user.is_anon) {
@@ -330,8 +329,6 @@ document.getElementById('profile-form').addEventListener('submit', async (e) =>
@@ -330,8 +329,6 @@ document.getElementById('profile-form').addEventListener('submit', async (e) =>
const box = document.getElementById('public-page-preview'); box.hidden = false;
const box = document.getElementById('public-page-preview'); box.hidden = false;
box.replaceChildren();
box.replaceChildren();
const h = document.createElement('h4'); h.textContent = currentUser?.display_name || currentUser?.username || 'Your name'; box.append(h);
const h = document.createElement('h4'); h.textContent = currentUser?.display_name || currentUser?.username || 'Your name'; box.append(h);
- const pronouns = document.getElementById('public-page-pronouns').value;
- if (pronouns) { const byline = document.createElement('p'); byline.textContent = pronouns; box.append(byline); }
const bio = document.createElement('p'); bio.textContent = document.getElementById('public-page-bio').value; box.append(bio);
const bio = document.createElement('p'); bio.textContent = document.getElementById('public-page-bio').value; box.append(bio);
for (const row of document.getElementById('public-page-links').value.split('\n').filter(Boolean)) { const a = document.createElement('p'); a.textContent = row; box.append(a); }
for (const row of document.getElementById('public-page-links').value.split('\n').filter(Boolean)) { const a = document.createElement('p'); a.textContent = row; box.append(a); }
};
};
@@ -343,7 +340,7 @@ document.getElementById('profile-form').addEventListener('submit', async (e) =>
@@ -343,7 +340,7 @@ document.getElementById('profile-form').addEventListener('submit', async (e) =>
const places = Object.fromEntries(['live', 'media', 'space', 'community'].map(key => [key, document.getElementById(`public-place-${key}`).checked]));
const places = Object.fromEntries(['live', 'media', 'space', 'community'].map(key => [key, document.getElementById(`public-place-${key}`).checked]));
save.disabled = true; msg.textContent = 'Saving…';
save.disabled = true; msg.textContent = 'Saving…';
try {
try {
- const data = await apiFetch('/api/auth/profile', { method: 'PUT', body: JSON.stringify({ public_page_bio: document.getElementById('public-page-bio').value, public_page_pronouns: document.getElementById('public-page-pronouns').value, public_page_links: links, public_page_places: places }) });
+ const data = await apiFetch('/api/auth/profile', { method: 'PUT', body: JSON.stringify({ public_page_bio: document.getElementById('public-page-bio').value, public_page_links: links, public_page_places: places }) });
currentUser = data.user; msg.textContent = 'Your public page is saved.'; preview();
currentUser = data.user; msg.textContent = 'Your public page is saved.'; preview();
} catch (err) { msg.textContent = err.message || 'Could not save your public page.'; }
} catch (err) { msg.textContent = err.message || 'Could not save your public page.'; }
save.disabled = false;
save.disabled = false;
@@ -225,10 +225,6 @@
@@ -225,10 +225,6 @@
<h3><span class="icon"><i class="fa-solid fa-address-card"></i></span> Your public page</h3>
<h3><span class="icon"><i class="fa-solid fa-address-card"></i></span> Your public page</h3>
<p class="muted">A link-in-bio page at <a id="public-page-url" href="https://openvibe.network/" target="_blank" rel="noopener">openvibe.network/@you</a>. Only information already public while signed out can appear here.</p>
<p class="muted">A link-in-bio page at <a id="public-page-url" href="https://openvibe.network/" target="_blank" rel="noopener">openvibe.network/@you</a>. Only information already public while signed out can appear here.</p>
<!-- The same fields as Edit Profile above (.form-field): they were bare labels and boxes in a line (11-09). -->
<!-- The same fields as Edit Profile above (.form-field): they were bare labels and boxes in a line (11-09). -->
- <div class="form-field">
- <label for="public-page-pronouns">Pronouns</label><input id="public-page-pronouns" maxlength="40" autocomplete="off">
- <div class="hint">Up to 40 characters.</div>
- </div>
<div class="form-field">
<div class="form-field">
<label for="public-page-bio">Bio</label><textarea id="public-page-bio" maxlength="280" rows="3"></textarea>
<label for="public-page-bio">Bio</label><textarea id="public-page-bio" maxlength="280" rows="3"></textarea>
<div class="hint">Up to 280 characters.</div>
<div class="hint">Up to 280 characters.</div>
@@ -408,7 +408,7 @@ router.get('/me', requireAuth, async (req, res) => {
@@ -408,7 +408,7 @@ router.get('/me', requireAuth, async (req, res) => {
// ── Update Profile ───────────────────────────────────────────
// ── Update Profile ───────────────────────────────────────────
router.put('/profile', requireAuth, async (req, res) => {
router.put('/profile', requireAuth, async (req, res) => {
const db = await getDb(req);
const db = await getDb(req);
- const { display_name, bio, avatar_url, email, profile_color, profile_public, public_page_bio, public_page_pronouns, public_page_links, public_page_places } = req.body;
+ const { display_name, bio, avatar_url, email, profile_color, profile_public, public_page_bio, public_page_links, public_page_places } = req.body;
const updates = [];
const updates = [];
const params = [];
const params = [];
@@ -444,11 +444,6 @@ router.put('/profile', requireAuth, async (req, res) => {
@@ -444,11 +444,6 @@ router.put('/profile', requireAuth, async (req, res) => {
if (text.length > 280) return res.status(400).json({ error: 'Your public page bio can be up to 280 characters' });
if (text.length > 280) return res.status(400).json({ error: 'Your public page bio can be up to 280 characters' });
updates.push('public_page_bio = ?'); params.push(text);
updates.push('public_page_bio = ?'); params.push(text);
}
}
- if (public_page_pronouns !== undefined) {
- const text = String(public_page_pronouns || '').trim();
- if (text.length > 40) return res.status(400).json({ error: 'Pronouns can be up to 40 characters' });
- updates.push('public_page_pronouns = ?'); params.push(text);
- }
if (public_page_links !== undefined) {
if (public_page_links !== undefined) {
if (!Array.isArray(public_page_links) || public_page_links.length > 8) return res.status(400).json({ error: 'Add up to 8 links' });
if (!Array.isArray(public_page_links) || public_page_links.length > 8) return res.status(400).json({ error: 'Add up to 8 links' });
const links = [];
const links = [];
@@ -470,8 +465,8 @@ router.put('/profile', requireAuth, async (req, res) => {
@@ -470,8 +465,8 @@ router.put('/profile', requireAuth, async (req, res) => {
if (updates.length === 0) return res.status(400).json({ error: 'No fields to update' });
if (updates.length === 0) return res.status(400).json({ error: 'No fields to update' });
- const publicPageTouched = public_page_bio !== undefined || public_page_pronouns !== undefined || public_page_links !== undefined || public_page_places !== undefined || profile_public !== undefined;
- const beforePage = publicPageTouched ? await db.prepare('SELECT username, profile_public, public_page_bio, public_page_pronouns, public_page_links, public_page_places FROM users WHERE id = ?').get(req.user.id) : null;
+ const publicPageTouched = public_page_bio !== undefined || public_page_links !== undefined || public_page_places !== undefined || profile_public !== undefined;
+ const beforePage = publicPageTouched ? await db.prepare('SELECT username, profile_public, public_page_bio, public_page_links, public_page_places FROM users WHERE id = ?').get(req.user.id) : null;
updates.push('updated_at = ov_now()');
updates.push('updated_at = ov_now()');
params.push(req.user.id);
params.push(req.user.id);
@@ -488,7 +483,6 @@ router.put('/profile', requireAuth, async (req, res) => {
@@ -488,7 +483,6 @@ router.put('/profile', requireAuth, async (req, res) => {
if (beforePage && publicPageTouched) {
if (beforePage && publicPageTouched) {
const json = (value) => { try { return typeof value === 'string' ? JSON.parse(value) : value; } catch { return null; } };
const json = (value) => { try { return typeof value === 'string' ? JSON.parse(value) : value; } catch { return null; } };
const changed = String(beforePage.public_page_bio || '') !== String(user.public_page_bio || '')
const changed = String(beforePage.public_page_bio || '') !== String(user.public_page_bio || '')
- || String(beforePage.public_page_pronouns || '') !== String(user.public_page_pronouns || '')
|| JSON.stringify(json(beforePage.public_page_links) || []) !== JSON.stringify(json(user.public_page_links) || [])
|| JSON.stringify(json(beforePage.public_page_links) || []) !== JSON.stringify(json(user.public_page_links) || [])
|| JSON.stringify(json(beforePage.public_page_places) || {}) !== JSON.stringify(json(user.public_page_places) || {})
|| JSON.stringify(json(beforePage.public_page_places) || {}) !== JSON.stringify(json(user.public_page_places) || {})
|| Number(beforePage.profile_public) !== Number(user.profile_public);
|| Number(beforePage.profile_public) !== Number(user.profile_public);
@@ -284,7 +284,7 @@ function createPublicProfiles({ db, selfToken = () => null, inventoryUrl = 'http
@@ -284,7 +284,7 @@ function createPublicProfiles({ db, selfToken = () => null, inventoryUrl = 'http
const rec = await usernames.lookup(db, name);
const rec = await usernames.lookup(db, name);
if (!rec) return null;
if (!rec) return null;
if (rec.renamed) return { moved: rec.current };
if (rec.renamed) return { moved: rec.current };
- const u = await db.prepare(`SELECT id, username, display_name, bio, public_page_bio, public_page_pronouns, public_page_links, public_page_places, profile_color, created_at, subject_id, profile_public,
+ const u = await db.prepare(`SELECT id, username, display_name, bio, public_page_bio, public_page_links, public_page_places, profile_color, created_at, subject_id, profile_public,
is_anon, merged_into, deleted_at FROM users WHERE id = ?`).get(rec.network_id);
is_anon, merged_into, deleted_at FROM users WHERE id = ?`).get(rec.network_id);
if (!u || Number(u.is_anon) || u.merged_into || u.deleted_at) return null;
if (!u || Number(u.is_anon) || u.merged_into || u.deleted_at) return null;
return { user: u };
return { user: u };
@@ -295,7 +295,7 @@ function createPublicProfiles({ db, selfToken = () => null, inventoryUrl = 'http
@@ -295,7 +295,7 @@ function createPublicProfiles({ db, selfToken = () => null, inventoryUrl = 'http
try { links = typeof u.public_page_links === 'string' ? JSON.parse(u.public_page_links) : (u.public_page_links || []); } catch {}
try { links = typeof u.public_page_links === 'string' ? JSON.parse(u.public_page_links) : (u.public_page_links || []); } catch {}
try { choices = { ...choices, ...(typeof u.public_page_places === 'string' ? JSON.parse(u.public_page_places) : (u.public_page_places || {})) }; } catch {}
try { choices = { ...choices, ...(typeof u.public_page_places === 'string' ? JSON.parse(u.public_page_places) : (u.public_page_places || {})) }; } catch {}
const places = Number(u.profile_public) ? await placesOf(u.username, choices) : { spaces: [], community: [], videos: [] };
const places = Number(u.profile_public) ? await placesOf(u.username, choices) : { spaces: [], community: [], videos: [] };
- return { username: u.username, display_name: u.display_name || u.username, pronouns: Number(u.profile_public) ? String(u.public_page_pronouns || '').slice(0, 40) : '', avatar_url: avatarUrl(u.username), profile_url: `${SITE}/@${encodeURIComponent(u.username)}`, bio: Number(u.profile_public) ? String(u.public_page_bio || '').slice(0, 280) : '', links: Number(u.profile_public) && Array.isArray(links) ? links.slice(0, 8) : [], places };
+ return { username: u.username, display_name: u.display_name || u.username, avatar_url: avatarUrl(u.username), profile_url: `${SITE}/@${encodeURIComponent(u.username)}`, bio: Number(u.profile_public) ? String(u.public_page_bio || '').slice(0, 280) : '', links: Number(u.profile_public) && Array.isArray(links) ? links.slice(0, 8) : [], places };
}
}
/** profile@1: what every site may show about a person. */
/** profile@1: what every site may show about a person. */
@@ -340,7 +340,7 @@ function createPublicProfiles({ db, selfToken = () => null, inventoryUrl = 'http
@@ -340,7 +340,7 @@ function createPublicProfiles({ db, selfToken = () => null, inventoryUrl = 'http
res.set('Cache-Control', cache.htmlHeaders({ maxAge: 60 }));
res.set('Cache-Control', cache.htmlHeaders({ maxAge: 60 }));
// A person's public profile lives on openvibe.network (the owner, 11-08: "OpenVibe.My is for users' stuff,
// A person's public profile lives on openvibe.network (the owner, 11-08: "OpenVibe.My is for users' stuff,
// the profiles should be on .Network"). openvibe.my is the person's own hub: /@name there moves here.
// the profiles should be on .Network"). openvibe.my is the person's own hub: /@name there moves here.
- res.send(renderPage(u, inv, SITE, page.places, page.pronouns));
+ res.send(renderPage(u, inv, SITE, page.places));
} catch (err) { next(err); }
} catch (err) { next(err); }
}
}
@@ -453,7 +453,7 @@ ${sorted.map((g) => {
@@ -453,7 +453,7 @@ ${sorted.map((g) => {
<p class="pf-note"><a href="${esc(all)}">${inv.more ? 'Every item' : 'This inventory'} on OpenVibe.Inventory</a> · <a href="${INVENTORY_SITE}/items">Every item there is</a></p></section>`;
<p class="pf-note"><a href="${esc(all)}">${inv.more ? 'Every item' : 'This inventory'} on OpenVibe.Inventory</a> · <a href="${INVENTORY_SITE}/items">Every item there is</a></p></section>`;
}
}
-function renderPage(u, inv, origin = SITE, places = { spaces: [], community: [], videos: [] }, pronouns = '') {
+function renderPage(u, inv, origin = SITE, places = { spaces: [], community: [], videos: [] }) {
const canonicalUrl = `${origin}/@${encodeURIComponent(u.username)}`;
const canonicalUrl = `${origin}/@${encodeURIComponent(u.username)}`;
const name = u.display_name || u.username;
const name = u.display_name || u.username;
const isPublic = !!inv;
const isPublic = !!inv;
@@ -524,7 +524,7 @@ a{color:var(--accent)}
@@ -524,7 +524,7 @@ a{color:var(--accent)}
.pf-who{flex:1;min-width:200px;padding-top:56px}
.pf-who{flex:1;min-width:200px;padding-top:56px}
.pf-who h1{margin:0;font-size:clamp(1.5rem,4vw,2.1rem);line-height:1.15;overflow-wrap:anywhere}
.pf-who h1{margin:0;font-size:clamp(1.5rem,4vw,2.1rem);line-height:1.15;overflow-wrap:anywhere}
.pf-name-badge{width:.85em;height:.85em;margin-right:.25em;vertical-align:-.08em;border-radius:6px;object-fit:contain}
.pf-name-badge{width:.85em;height:.85em;margin-right:.25em;vertical-align:-.08em;border-radius:6px;object-fit:contain}
-.pf-handle{margin:.2em 0 0;color:var(--text-secondary)}.pf-pronouns{display:inline-block;margin-left:6px;padding:1px 9px;border:1px solid var(--border);border-radius:999px;font-size:.82em;vertical-align:1px}.pf-places a .pf-live{display:inline-flex;align-items:center;gap:6px;color:var(--live-red,#e5484d);font-weight:700}.pf-live::before{content:'';width:8px;height:8px;border-radius:50%;background:currentColor}
+.pf-handle{margin:.2em 0 0;color:var(--text-secondary)}.pf-places a .pf-live{display:inline-flex;align-items:center;gap:6px;color:var(--live-red,#e5484d);font-weight:700}.pf-live::before{content:'';width:8px;height:8px;border-radius:50%;background:currentColor}
.pf-bio{margin:0;padding:0 24px 18px;color:var(--text-primary);line-height:1.6;white-space:pre-line;overflow-wrap:anywhere;max-width:72ch}
.pf-bio{margin:0;padding:0 24px 18px;color:var(--text-primary);line-height:1.6;white-space:pre-line;overflow-wrap:anywhere;max-width:72ch}
.pf-stats{list-style:none;display:flex;flex-wrap:wrap;gap:8px;margin:0;padding:0 24px 22px}
.pf-stats{list-style:none;display:flex;flex-wrap:wrap;gap:8px;margin:0;padding:0 24px 22px}
.pf-stats li{display:flex;flex-direction:column;gap:2px;padding:8px 14px;border:1px solid var(--border);border-radius:12px;background:var(--bg-card);min-width:110px}
.pf-stats li{display:flex;flex-direction:column;gap:2px;padding:8px 14px;border:1px solid var(--border);border-radius:12px;background:var(--bg-card);min-width:110px}
@@ -578,7 +578,7 @@ ${require('openvibe-shared/frame').noscriptNav({ name: 'OpenVibe.Network', links
@@ -578,7 +578,7 @@ ${require('openvibe-shared/frame').noscriptNav({ name: 'OpenVibe.Network', links
<div class="pf-banner" aria-hidden="true"></div>
<div class="pf-banner" aria-hidden="true"></div>
<div class="pf-head">
<div class="pf-head">
<div class="pf-pic"><img class="pf-avatar" src="${esc(avatarUrl(u.username))}" alt="" width="112" height="112">${hatGlyph ? `<span class="pf-hat" title="${esc(hat.name)}" aria-hidden="true">${esc(hatGlyph)}</span>` : ''}</div>
<div class="pf-pic"><img class="pf-avatar" src="${esc(avatarUrl(u.username))}" alt="" width="112" height="112">${hatGlyph ? `<span class="pf-hat" title="${esc(hat.name)}" aria-hidden="true">${esc(hatGlyph)}</span>` : ''}</div>
-<div class="pf-who"><h1>${badgeId ? `<img class="pf-name-badge" src="https://openvibe.media/o/${badgeId}" alt="" title="${esc(badge.name)}" width="32" height="32">` : ''}${fxClass ? `<span class="ov-fx ${fxClass}" title="${esc(fx.name)}">${esc(name)}</span>` : esc(name)}</h1><p class="pf-handle">@${esc(u.username)}${isPublic && pronouns ? ` <span class="pf-pronouns">${esc(pronouns)}</span>` : ''}</p></div>
+<div class="pf-who"><h1>${badgeId ? `<img class="pf-name-badge" src="https://openvibe.media/o/${badgeId}" alt="" title="${esc(badge.name)}" width="32" height="32">` : ''}${fxClass ? `<span class="ov-fx ${fxClass}" title="${esc(fx.name)}">${esc(name)}</span>` : esc(name)}</h1><p class="pf-handle">@${esc(u.username)}</p></div>
</div>
</div>
${bio ? `<p class="pf-bio">${esc(bio)}</p>` : ''}
${bio ? `<p class="pf-bio">${esc(bio)}</p>` : ''}
${isPublic && Array.isArray(publicLinks) && publicLinks.length ? `<nav aria-label="${esc(name)}'s links"><ul class="pf-links">${publicLinks.slice(0, 8).map((link) => { let href = ''; try { const parsed = new URL(link.url); if (parsed.protocol === 'https:' && !parsed.username && !parsed.password) href = parsed.href; } catch {} return href ? `<li><a rel="me noopener noreferrer" href="${esc(href)}">${esc(String(link.label || '').slice(0, 40))}</a></li>` : ''; }).join('')}</ul></nav>` : ''}
${isPublic && Array.isArray(publicLinks) && publicLinks.length ? `<nav aria-label="${esc(name)}'s links"><ul class="pf-links">${publicLinks.slice(0, 8).map((link) => { let href = ''; try { const parsed = new URL(link.url); if (parsed.protocol === 'https:' && !parsed.username && !parsed.password) href = parsed.href; } catch {} return href ? `<li><a rel="me noopener noreferrer" href="${esc(href)}">${esc(String(link.label || '').slice(0, 40))}</a></li>` : ''; }).join('')}</ul></nav>` : ''}