diff --git a/server/shell.ts b/server/shell.ts index 0ebad7d..d6ec5c2 100644 --- a/server/shell.ts +++ b/server/shell.ts @@ -884,17 +884,20 @@ export function renderShell(opts: ShellOptions): string { } else { var userSlug = ''; try { if (session && session.claims && session.claims.username) userSlug = session.claims.username; } catch(e) {} - var homeHref = userSlug - ? 'https://' + userSlug + '.rspace.online/' + var mySpaceHref = userSlug + ? 'https://' + userSlug.toLowerCase() + '.rspace.online/' : 'https://rspace.online/'; - var homeLabel = userSlug ? 'Return to ' + userSlug : 'Return to rSpace'; + var mySpaceLabel = userSlug ? 'Go to ' + userSlug + '\\\'s Space' : 'Go to rSpace'; gate.innerHTML = '
' + '
🔒
' + '

Private Space

' + '

You don\\\'t have access to ' + slug + '.

' + - '

This space is private. Ask the owner to invite you as a member.

' + - '' + homeLabel + '' + + '

This space is private. You can request access from the owner.

' + + '
' + + '' + + '' + mySpaceLabel + '' + + '
' + '
'; } @@ -914,6 +917,44 @@ export function renderShell(opts: ShellOptions): string { } }); } + + // Request access button handler + var reqBtn = document.getElementById('gate-request-access'); + if (reqBtn) reqBtn.addEventListener('click', function() { + reqBtn.disabled = true; + reqBtn.textContent = 'Requesting...'; + fetch('/api/spaces/' + encodeURIComponent(slug) + '/access-requests', { + method: 'POST', + headers: { + 'Authorization': 'Bearer ' + session.accessToken, + 'Content-Type': 'application/json' + }, + body: JSON.stringify({}) + }) + .then(function(r) { return r.json().then(function(d) { return { status: r.status, data: d }; }); }) + .then(function(res) { + if (res.status === 201) { + reqBtn.textContent = 'Request Sent ✓'; + reqBtn.style.opacity = '0.7'; + var hint = gate.querySelector('.access-gate__hint'); + if (hint) hint.textContent = 'Your request has been sent to the space owner. You\\\'ll be notified when it\\\'s approved.'; + } else if (res.status === 409) { + reqBtn.textContent = 'Request Pending'; + reqBtn.style.opacity = '0.7'; + var hint = gate.querySelector('.access-gate__hint'); + if (hint) hint.textContent = 'You already have a pending access request for this space.'; + } else { + reqBtn.textContent = 'Request Access'; + reqBtn.disabled = false; + alert(res.data.error || 'Failed to send request'); + } + }) + .catch(function() { + reqBtn.textContent = 'Request Access'; + reqBtn.disabled = false; + alert('Network error — please try again'); + }); + }); } })(); @@ -1755,6 +1796,13 @@ const ACCESS_GATE_CSS = ` transition: opacity 0.15s, transform 0.15s; } .access-gate__btn:hover { opacity: 0.9; transform: translateY(-1px); } +.access-gate__btn:disabled { opacity: 0.6; cursor: default; transform: none; } +.access-gate__btn--secondary { + background: transparent; border: 1px solid var(--rs-border, rgba(255,255,255,0.15)); + color: var(--rs-text-secondary, #94a3b8); +} +.access-gate__btn--secondary:hover { border-color: var(--rs-border-strong, rgba(255,255,255,0.3)); color: var(--rs-text-primary, #e0e0e0); } +.access-gate__actions { display: flex; flex-direction: column; gap: 0.75rem; align-items: center; } `; const WELCOME_CSS = `