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.
' +
+ '
' +
'
';
}
@@ -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 = `