Files
Cs2-Mapveto/client/src/pages/AccessPages.jsx
T
SPP UserandClaude Opus 5.5 4152f9c0a6 Organisator kann im selben Browser selbst mitspielen
Ein Aufrufer darf Organisator- und Spieler-Zugang kombinieren
(Authorization + X-Player-Token). Berechtigungen prüfen jetzt einzelne
Merkmale statt einer Rangstufe. In der Lobby gibt es „Selbst mitspielen?“,
Teamlinks funktionieren auch im Organisator-Browser. Online-Status wird
direkt nach dem Beitritt aktualisiert.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 17:53:39 +01:00

102 lines
3.9 KiB
React

import { useEffect, useState } from 'react';
import { Link, useNavigate, useParams } from 'react-router-dom';
import { api } from '../lib/api.js';
import { getAccess, setAccess, setPlayerToken } from '../lib/session.js';
function useCode() {
const { code } = useParams();
const [info, setInfo] = useState(null);
const [error, setError] = useState(null);
useEffect(() => {
api('/access', { method: 'POST', body: { code } }).then(setInfo).catch((e) => setError(e.message));
}, [code]);
return { code, info, error };
}
function alreadyPlayer(access) {
if (!access) return false;
if (access.kind === 'organizer') return Boolean(access.playerToken);
return access.kind === 'player';
}
export function JoinPage() {
const { code, info, error } = useCode();
const navigate = useNavigate();
const [nickname, setNickname] = useState('');
const [steamId, setSteamId] = useState('');
const [submitError, setSubmitError] = useState(null);
const [busy, setBusy] = useState(false);
useEffect(() => {
if (!info) return;
if (info.access === 'spectator') navigate(`/watch/${code}`, { replace: true });
// Schon als Spieler dabei? Dann direkt zur Lobby. Organisator ohne Team und Zuschauer dürfen beitreten.
else if (alreadyPlayer(getAccess(info.matchId))) navigate(`/m/${info.matchId}`, { replace: true });
}, [info, code, navigate]);
if (error) return <AccessError message={error} />;
if (!info) return <p className="muted">Prüfe Code …</p>;
const submit = async (e) => {
e.preventDefault();
setBusy(true);
setSubmitError(null);
try {
const res = await api(`/matches/${info.matchId}/join`, { method: 'POST', body: { code, nickname, steamId } });
const existing = getAccess(info.matchId);
// Der Organisator behält seinen Zugang und bekommt den Spieler-Zugang dazu.
if (existing?.kind === 'organizer') setPlayerToken(info.matchId, res.playerToken);
else setAccess(info.matchId, { token: res.playerToken, kind: 'player' });
navigate(`/m/${info.matchId}`);
} catch (err) {
setSubmitError(err.message);
setBusy(false);
}
};
return (
<form className={`join-card team-${info.team}`} onSubmit={submit}>
<p className="muted">{info.title}</p>
<h1>Beitreten als {info.teamName}</h1>
<label className="field">
<span>Spielername</span>
<input value={nickname} onChange={(e) => setNickname(e.target.value)} required minLength={2} maxLength={24} autoFocus />
</label>
<label className="field">
<span>SteamID64 (optional)</span>
<input value={steamId} onChange={(e) => setSteamId(e.target.value)} inputMode="numeric" placeholder="7656119…" pattern="\d{17}" />
<small className="hint">Hilft dem Organisator, dich auf dem Server zuzuordnen.</small>
</label>
{submitError && <div className="alert alert-error">{submitError}</div>}
<button className="btn btn-primary btn-lg" disabled={busy}>{busy ? 'Trete bei …' : `${info.teamName} beitreten`}</button>
</form>
);
}
export function WatchPage() {
const { code, info, error } = useCode();
const navigate = useNavigate();
useEffect(() => {
if (!info) return;
if (info.access !== 'spectator') {
navigate(`/join/${code}`, { replace: true });
return;
}
// Spieler oder Organisator behalten ihren stärkeren Zugang.
if (!getAccess(info.matchId)) setAccess(info.matchId, { token: code, kind: 'spectator' });
navigate(`/m/${info.matchId}`, { replace: true });
}, [info, code, navigate]);
if (error) return <AccessError message={error} />;
return <p className="muted">Öffne Zuschauerzugang …</p>;
}
function AccessError({ message }) {
return (
<div className="empty">
<h2>Code nicht gültig</h2>
<p className="muted">{message}. Prüfe den Link oder frag den Organisator nach einem neuen Code.</p>
<Link to="/" className="btn">Zur Übersicht</Link>
</div>
);
}