feat: guest passage — slip through as a wanderer
POST /auth/guest mints a real user row (wanderer-<4 hex>, collision retry, unusable random password) and issues the normal session cookie, per-IP rate limited at 5/hour. EnterPage gains the guest action; the séance shows a dismissible claim-a-name note for wanderer- users. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -108,6 +108,10 @@ function SeanceSession({ username }: { username: string }) {
|
||||
const { t, i18n } = useTranslation()
|
||||
|
||||
const [departed, setDeparted] = useState(false)
|
||||
// Guests are recognized by their generated name prefix — no backend flag.
|
||||
// Dismissal is per-visit on purpose: the nudge should return next séance.
|
||||
const isWanderer = username.startsWith('wanderer-')
|
||||
const [wandererNoteDismissed, setWandererNoteDismissed] = useState(false)
|
||||
const [question, setQuestion] = useState('')
|
||||
const [dossierOpen, setDossierOpen] = useState(true)
|
||||
const [view, setView] = useState<BoardView>('board')
|
||||
@@ -306,6 +310,22 @@ function SeanceSession({ username }: { username: string }) {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* ---- wanderer note: guests can claim a permanent name ---- */}
|
||||
{isWanderer && !wandererNoteDismissed && (
|
||||
<div className="seance-wanderer-note" role="note">
|
||||
<span>{t('seance.wanderer.note')}</span>{' '}
|
||||
<Link to="/enter">{t('seance.wanderer.claim')}</Link>
|
||||
<button
|
||||
type="button"
|
||||
className="wanderer-note-dismiss"
|
||||
aria-label={t('seance.wanderer.dismiss')}
|
||||
onClick={() => setWandererNoteDismissed(true)}
|
||||
>
|
||||
×
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* ---- main: board + side column ---- */}
|
||||
<main className="seance-main">
|
||||
<section className="seance-board-wrap">
|
||||
|
||||
Reference in New Issue
Block a user