openvibers/openvibe.community Commits

Author avatars load at the size the page draws them (#222)

OP OpenVibers committed 12 days ago · bb94b6a036 · 4 files, +28 −4

Changes

ddiff --git a/server/render/board.js b/server/render/board.js
iindex 4867d16..dfd9f76 100644
---- a/server/render/board.js
++++ b/server/render/board.js
@@@ -12,7 +12,7 @@ const ld = require('./jsonld');
const { renderPage } = require('./layout');
const { escapeHtml: esc } = require('./highlight');
const { markdownToText } = require('./markdown');
--const { timeTag, fmtDate, num, avatarUrl, authorHtml, wearName } = require('./pages');
++const { timeTag, fmtDate, num, avatarUrl, mediaW, authorHtml, wearName } = require('./pages');
const f = require('./forum');
const STYLE_LABELS = { forum: 'Classic', feed: 'Feed' }; // the two styles, as the Forum page and the settings name them
@@@ -176,7 +176,7 @@ ${viewer.can_moderate ? settingsForm(space, groups) + moderatorsBox(space, moder
function authorPanel(p) {
const a = p.author;
if (!a) return '<aside class="postbit-author"><span class="postbit-name">Anonymous</span></aside>';
-- const pic = avatarUrl(a, 96);
++ const pic = mediaW(avatarUrl(a, 96), 160);
const name = a.display_name || a.username || (a.is_ai ? 'AI' : a.is_system ? 'OpenVibe' : 'Someone');
const initial = esc(String(name).trim()[0] || '?').toUpperCase();
const st = p.author_stats;
ddiff --git a/server/render/pages.js b/server/render/pages.js
iindex 06499d2..7000162 100644
---- a/server/render/pages.js
++++ b/server/render/pages.js
@@@ -28,6 +28,12 @@ function mediaAbs(u) {
if (/^https?:\/\//i.test(u)) return u;
return `${config.mediaUrl}${u.startsWith('/') ? '' : '/'}${u}`;
}
++// A Media object is stored at full size: ask its resize ladder for the width drawn here (96 for the 22 px author
++// avatar, 160 for the 64 px postbit one). Any other picture address — the network's avatar endpoint, a picture that
++// already carries a query — is used unchanged.
++function mediaW(u, w) {
++ return /^https:\/\/openvibe\.media\/o\/med_[0-9A-Za-z]+$/.test(String(u || '')) ? `${u}?w=${w}` : u;
++}
/** A screenshot's Media address when the stored row has no screenshot_url. */
function screenshotFallback(slug) { return `${config.mediaUrl}/p/${encodeURIComponent(slug)}/screenshot`; }
@@@ -76,7 +82,7 @@ function authorHtml(p, { link = true } = {}) {
// picture, or a generated initial), so an author looks the same here as on every other OpenVibe site.
const netAvatar = !avatarSrc && p.username ? `${config.networkUrl}/avatar/${encodeURIComponent(p.username)}?s=44` : null;
const avatar = (avatarSrc || netAvatar)
-- ? `<img class="avatar" src="${esc(avatarSrc || netAvatar)}" alt="" loading="lazy" width="22" height="22">`
++ ? `<img class="avatar" src="${esc(mediaW(avatarSrc || netAvatar, 96))}" alt="" loading="lazy" width="22" height="22">`
: `<span class="avatar avatar-letter" aria-hidden="true">${initial}</span>`;
const wearer = wearerOf(p);
const inner = `${avatar}${wearer ? wearName(p, name) : `<span>${esc(name)}</span>`}`;
@@@ -597,4 +603,4 @@ function errorPage({ status = 500, title = 'Something went wrong', message = '',
return renderPage({ title, description: message || title, canonicalPath: '/', robots: 'noindex,nofollow', footerVariant: 'compact', body });
}
--module.exports = { homePage, updatesPage, browsePage, pastePage, newPage, myPage, errorPage, pasteCard, cardGrid, timeAgo, timeTag, fmtDate, num, authorHtml, avatarUrl, wearName };
++module.exports = { homePage, updatesPage, browsePage, pastePage, newPage, myPage, errorPage, pasteCard, cardGrid, timeAgo, timeTag, fmtDate, num, authorHtml, avatarUrl, mediaW, wearName };
ddiff --git a/test/forum-style.test.js b/test/forum-style.test.js
iindex b1d321f..0083dbb 100644
---- a/test/forum-style.test.js
++++ b/test/forum-style.test.js
@@@ -50,6 +50,14 @@ const { boot, check, done } = require('./helpers/app');
assert.ok(!page.includes('class="thread-score"'), 'no score column');
});
++ await check('the postbit avatar asks Media for the width it draws (the 64 px picture: 160)', async () => {
++ const obj = 'https://openvibe.media/o/med_01M644DY8BFX4H7MTRKPGG052R';
++ await t.db.prepare('UPDATE subject_projection SET avatar_url = ? WHERE subject_id = ?').run(obj, alex.subject_id);
++ const html = (await call('/s/general/t/welcome-thread')).text;
++ assert.ok(html.includes(`<img class="postbit-avatar" src="${obj}?w=160"`), 'the 64 px postbit avatar asks for 160');
++ await t.db.prepare('UPDATE subject_projection SET avatar_url = NULL WHERE subject_id = ?').run(alex.subject_id);
++ });
++
await check('the board index: groups in order, every space with topics, posts and the last post', async () => {
const r = (await call('/api/v1/spaces')).json();
const general = r.groups[0].spaces.find((s) => s.slug === 'general');
ddiff --git a/test/pulse.test.js b/test/pulse.test.js
iindex 6a54da6..8cd69bb 100644
---- a/test/pulse.test.js
++++ b/test/pulse.test.js
@@@ -188,6 +188,16 @@ const { boot, check, done } = require('./helpers/app');
assert.ok(r.text.includes('href="/pulse?origin=system"'), 'system items exist here, so the tab shows');
});
++ await check('an author avatar on a Media object asks for the width the page draws; other addresses are left alone', () => {
++ const { authorHtml } = require('../server/render/pages');
++ const obj = 'https://openvibe.media/o/med_01M644DY8BFX4H7MTRKPGG052R';
++ assert.ok(authorHtml({ display_name: 'Alex', username: 'alex', avatar_url: obj }).includes(`<img class="avatar" src="${obj}?w=96"`), 'the 22 px author avatar asks for 96');
++ const sized = authorHtml({ display_name: 'Alex', username: 'alex', avatar_url: `${obj}?w=64` });
++ assert.ok(sized.includes(`src="${obj}?w=64"`) && !sized.includes('?w=64?w='), 'an already sized URL is used as stored, with no second query');
++ const relative = authorHtml({ display_name: 'Alex', username: 'alex', avatar_url: '/data/avatars/alex.png' });
++ assert.ok(relative.includes('/data/avatars/alex.png"') && !relative.includes('/data/avatars/alex.png?w='), 'a non-Media avatar is used unchanged');
++ });
++
await t.close();
done();
})();