openvibers/openvibe.chat Commits

A party's invitee who is not signed in is invited, not shown a broken room (#398)

OP OpenVibers committed 6 days ago · 3b9c78431f · 2 files, +43 −6

The party is a private room: a signed-out holder of the link saw No such room, 0 watching and a chat box that could
not post. Now: the host's invitation over the picture with one move, Sign in to join (back to this link, its key
included); no remote, reactions, composer, count or socket; the player's controls off (it would not follow the party).

Changes

ddiff --git a/public/web/party.css b/public/web/party.css
iindex ed5fefb..ddd8aeb 100644
---- a/public/web/party.css
++++ b/public/web/party.css
@@@ -57,6 +57,14 @@ main[data-party] [hidden] { display: none !important; }
background: rgba(0, 0, 0, .66); color: #fff; font-size: 12.5px; font-weight: 700; pointer-events: none;
}
.pt-floats { position: absolute; inset: 0; pointer-events: none; overflow: hidden; }
++/* A guest (the link's holder, not signed in): the invitation and one move over the picture. */
++.pt-frame.is-guest .pt-veil { pointer-events: auto; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 14px; padding: 16px; background: rgba(4, 8, 18, .72); }
++.pt-veil-line { max-width: 28ch; }
++.pt-join { display: inline-flex; align-items: center; justify-content: center; min-height: 44px; padding: 0 22px; border-radius: 999px; background: var(--pt-accent); color: var(--pt-on-accent); font-size: 15px; font-weight: 800; text-decoration: none; }
++.pt-join:hover { filter: brightness(1.08); }
++.pt-guest-note { margin: 0; color: var(--pt-muted); font-size: 14px; line-height: 1.5; }
++.pt-guest-note a { font-weight: 700; }
++main[data-party].is-guest [data-party-chat] { min-height: 0; flex: 0 0 auto; align-self: start; }
.pt-float { position: absolute; bottom: 12px; font-size: 30px; animation: pt-rise 2.4s cubic-bezier(.2, .7, .3, 1) forwards; }
@keyframes pt-rise { 0% { transform: translateY(0) scale(.7); opacity: 0; } 12% { opacity: 1; transform: translateY(-14px) scale(1.05); } 100% { transform: translateY(-190px) scale(1); opacity: 0; } }
@media (prefers-reduced-motion: reduce) { .pt-float { animation: pt-fade 1.6s ease forwards; } @keyframes pt-fade { 0%, 70% { opacity: 1; } 100% { opacity: 0; } } }
ddiff --git a/public/web/party.js b/public/web/party.js
iindex 9b87b7f..7f331db 100644
---- a/public/web/party.js
++++ b/public/web/party.js
@@@ -69,12 +69,21 @@
return data.invite_url || ('https://openvibe.party/p/' + encodeURIComponent(slug) + (k ? '?k=' + encodeURIComponent(k) : ''));
}
++ // ── a guest: the link's holder who has not signed in ─────────────────────────
++ // A party is a private room: its socket and its chat need a person. A guest sees the picture and one move, Sign in to
++ // join, which comes straight back to this link (its key included). Nothing that would only fail is drawn: no remote,
++ // no reactions, no composer, no count of who is watching, no socket (it answered "No such room").
++ var guest = !token();
++ var joinHref = '/auth/login?next=' + encodeURIComponent(location.pathname + location.search);
++ function joinLink(cls, text) { var a = el('a', cls, text); a.href = joinHref; return a; }
++ if (guest) root.classList.add('is-guest');
++
// ── the stage ────────────────────────────────────────────────────────────────
var video = el('video');
video.setAttribute('data-ov-player', '');
video.setAttribute('playsinline', '');
video.preload = 'auto';
-- video.controls = true;
++ video.controls = !guest; // a guest's player would not follow the party: the invitation stands over it
var src = typeof (clock && clock.media) === 'string' ? clock.media : (typeof data.media === 'string' ? data.media : null);
if (src) video.src = src;
var frame = el('div', 'pt-frame');
@@@ -103,8 +112,8 @@
if (invite) { var h1 = head.querySelector('h1'); if (h1 && h1.nextSibling) head.insertBefore(invite, h1.nextSibling); else head.appendChild(invite); }
stage.appendChild(head);
stage.appendChild(frame);
-- stage.appendChild(bar);
-- if (remote) bar.appendChild(remote);
++ if (!guest) stage.appendChild(bar);
++ if (remote && !guest) bar.appendChild(remote);
var react = el('div', 'pt-react');
react.setAttribute('aria-label', 'React');
REACTIONS.forEach(function (r) {
@@@ -126,6 +135,15 @@
function holder() { return lease && lease.holder ? lease.holder : { kind: 'nobody', subject: null }; }
function iHold() { var h = holder(); return !!(self && h.kind === 'person' && h.subject === self.subject); }
function paintVeil() {
++ if (guest) {
++ veil.textContent = '';
++ var host = data.host && data.host.name ? data.host.name : null;
++ veil.appendChild(el('span', 'pt-veil-line', host ? host + ' invited you to watch together' : 'You are invited to watch together'));
++ veil.appendChild(joinLink('pt-join', 'Sign in to join'));
++ veil.hidden = false;
++ frame.classList.add('is-waiting', 'is-guest');
++ return;
++ }
var text = '';
if (!ws || ws.readyState !== 1) text = joined ? 'Reconnecting…' : 'Joining the party…';
else if (!src) text = 'This party has nothing to play yet.';
@@@ -170,7 +188,7 @@
remote.appendChild(p);
}
function paintRemote() {
-- if (!remote) return;
++ if (!remote || guest) return;
var h = holder(), mine = iHold(), isOwner = !!(self && self.subject === owner);
var takeBack = { label: 'Take it back', run: function () { send({ type: 'lease.take' }); } };
if (!self) return pill(h.kind === 'person' ? nameOf(h.subject) + ' has the remote' : 'Sign in to share the remote', []);
@@@ -193,6 +211,11 @@
function paintPeople() {
if (!people) return;
people.textContent = '';
++ if (guest) {
++ people.appendChild(el('h2', 'pt-h', 'Watching'));
++ people.appendChild(el('p', 'pt-guest-note', 'Sign in to see who is here.'));
++ return;
++ }
var watching = (lease && lease.watchers) || [];
var all = Object.keys(names);
watching.forEach(function (s) { if (all.indexOf(s) < 0) all.push(s); });
@@@ -229,7 +252,12 @@
var go = el('button', 'pt-say-btn', 'Send');
go.type = 'submit';
form.appendChild(input); form.appendChild(go);
-- if (chat) {
++ if (chat && guest) {
++ chat.appendChild(el('h2', 'pt-h', 'Chat'));
++ var note = el('p', 'pt-guest-note', 'The chat is for the people in the party. ');
++ note.appendChild(joinLink(null, 'Sign in to join'));
++ chat.appendChild(note);
++ } else if (chat) {
chat.appendChild(el('h2', 'pt-h', 'Chat'));
chat.appendChild(thread);
chat.appendChild(form);
@@@ -279,6 +307,7 @@
// ── the socket ───────────────────────────────────────────────────────────────
function connect() {
++ if (guest) { paintVeil(); return; }
if (!window.WebSocket) { paintVeil(); return; }
ws = new WebSocket((location.protocol === 'https:' ? 'wss://' : 'ws://') + location.host + '/ws/chat');
ws.onopen = function () { delay = 1000; send({ type: 'join_room', room: slug, token: token() }); paintVeil(); };
@@@ -337,7 +366,7 @@
video.addEventListener('play', function () { everPlayed = true; paintVeil(); });
// ── invite ───────────────────────────────────────────────────────────────────
-- if (invite) {
++ if (invite && !guest) {
var ib = el('button', 'pt-invite', 'Invite');
ib.type = 'button';
ib.addEventListener('click', function () {