Profile build-out (avatar + bio) + shareable bio page with QR
- Profile pane: avatar upload + bio, with a link to your public page - Wall becomes a bio page: avatar, bio, scannable join QR, line ladder, join CTA - qrcode npm dep; /api/qr renders SVG QR server-side (CSP-clean img) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
+38
-1
@@ -1057,8 +1057,45 @@
|
||||
: 'Not set yet. Until you set one, your tour slot is skipped.';
|
||||
}
|
||||
async function loadLineBanner() {
|
||||
try { fillLineBanner(await (await fetch('/api/me')).json()); } catch (e) {}
|
||||
try {
|
||||
const a = await (await fetch('/api/me')).json();
|
||||
fillLineBanner(a);
|
||||
fillProfileDetails(a);
|
||||
} catch (e) {}
|
||||
}
|
||||
function fillProfileDetails(a) {
|
||||
if (!a) return;
|
||||
if (a.bio) $('pfBio').value = a.bio;
|
||||
if (a.avatarUrl) { const p = $('pfAvatarPrev'); p.src = a.avatarUrl; p.hidden = false; }
|
||||
if (a.username) {
|
||||
const link = location.origin + '/wall/' + a.username;
|
||||
$('pfBioLink').textContent = link;
|
||||
$('pfViewBio').hidden = false;
|
||||
$('pfViewBio').href = '/wall/' + a.username;
|
||||
}
|
||||
}
|
||||
let pfAvatar; // pending avatar url
|
||||
$('pfAvatarBtn').addEventListener('click', () => $('pfAvatarFile').click());
|
||||
$('pfAvatarFile').addEventListener('change', async () => {
|
||||
const f = $('pfAvatarFile').files[0];
|
||||
if (!f) return;
|
||||
$('pfAvatarInfo').textContent = 'Uploading…';
|
||||
try {
|
||||
const r = await (await fetch('/api/my/upload', { method: 'POST', headers: { 'Content-Type': f.type }, body: f })).json();
|
||||
if (r.error) { $('pfAvatarInfo').textContent = r.error; $('pfAvatarFile').value = ''; return; }
|
||||
pfAvatar = r.url;
|
||||
$('pfAvatarInfo').textContent = 'Uploaded — save to apply.';
|
||||
const p = $('pfAvatarPrev'); p.src = r.url; p.hidden = false;
|
||||
} catch (e) { $('pfAvatarInfo').textContent = 'Upload failed.'; }
|
||||
$('pfAvatarFile').value = '';
|
||||
});
|
||||
$('pfDetailsSave').addEventListener('click', busy2($('pfDetailsSave'), async () => {
|
||||
const body = { bio: $('pfBio').value };
|
||||
if (pfAvatar) body.avatarUrl = pfAvatar;
|
||||
const r = await api('/api/my/profile-details', body);
|
||||
IAP.status('Profile saved.', 'ok');
|
||||
if (r.account) fillProfileDetails(r.account);
|
||||
}));
|
||||
$('lbUploadBtn').addEventListener('click', () => $('lbFile').click());
|
||||
$('lbFile').addEventListener('change', async () => {
|
||||
const f = $('lbFile').files[0];
|
||||
|
||||
@@ -349,6 +349,12 @@ textarea{resize:vertical;font:inherit}
|
||||
/* ── welcome tour (gauntlet): framed line sites + countdown ── */
|
||||
.lgate-frame{flex:1;border:0;width:100%;background:#fff;min-height:0}
|
||||
.ggmeta{padding:8px 16px;background:var(--panel-solid);border-bottom:1px solid var(--line)}
|
||||
/* ── bio header (wall/profile page) ── */
|
||||
.bio-head{display:flex;gap:22px;align-items:center;flex-wrap:wrap;padding:8px 0 4px}
|
||||
.bio-avatar{width:96px;height:96px;border-radius:50%;object-fit:cover;border:2px solid var(--mint);flex:0 0 auto}
|
||||
.bio-meta{flex:1;min-width:220px}
|
||||
.bio-qr{flex:0 0 auto;background:var(--panel);border:1px solid var(--line-strong);border-radius:14px;padding:10px}
|
||||
.bio-qr img{display:block;border-radius:8px;background:#eef7f3}
|
||||
/* ── wall page ── */
|
||||
.wall-card{background:var(--panel);border:1px solid var(--line);border-radius:var(--radius);padding:16px;text-align:center}
|
||||
.wall-card img{max-width:100%;border-radius:10px;border:1px solid var(--line-strong)}
|
||||
|
||||
@@ -12,7 +12,12 @@
|
||||
IAP.$('wallJoin').href = '/my';
|
||||
return;
|
||||
}
|
||||
title.innerHTML = 'The <em>' + String(w.name).replace(/[&<>]/g, '') + '</em> line';
|
||||
const safeName = String(w.name).replace(/[&<>]/g, '');
|
||||
title.innerHTML = safeName;
|
||||
IAP.$('bioHead').hidden = false;
|
||||
if (w.avatarUrl) { const av = IAP.$('bioAvatar'); av.src = w.avatarUrl; av.hidden = false; }
|
||||
IAP.$('bioText').textContent = w.bio || 'Building a team on InstantAdPay — join through this page and you\'re in my line.';
|
||||
if (w.qrUrl) IAP.$('bioQr').src = w.qrUrl;
|
||||
IAP.$('wallCtaHead').textContent = 'Join ' + w.name + '’s line';
|
||||
IAP.$('wallJoin').href = w.joinUrl;
|
||||
const grid = IAP.$('wallGrid');
|
||||
|
||||
Reference in New Issue
Block a user