Something went wrong. Try again.
A Social Deduction / Deception Game
Something went wrong. Try again.
3.6 kB · 102 lines
TSX
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103// Post-login gate, shown once when a signed-in player has no actor.rpg.sprite/self record yet. They// pick how to get one: build their own at rpg.actor (links out, then re-check), or clone the default// actor into their repo (POST /api/sprite/default — uses the sprite-write scope login granted because// they had no sprite). Either way, once a sprite exists the app falls through to the mode picker.
import { useState } from 'react'import { hasActorSprite } from './sprite-import'
export function SpriteSetup({ did, onReady, onSignOut,}: { did: string /** A sprite now exists for this player — proceed into the game. */ onReady: () => void onSignOut: () => void}) { const [busy, setBusy] = useState<null | 'default' | 'recheck'>(null) const [error, setError] = useState<string | null>(null)
async function useDefault() { setBusy('default') setError(null) try { const res = await fetch('/api/sprite/default', { method: 'POST', credentials: 'include' }) const data = (await res.json().catch(() => ({}))) as { error?: string } if (!res.ok) { setError(`couldn’t set the default actor: ${data.error ?? res.status}`) return } onReady() } catch (e) { setError(e instanceof Error ? e.message : String(e)) } finally { setBusy(null) } }
async function recheck() { setBusy('recheck') setError(null) try { if (await hasActorSprite(did)) onReady() else setError('still no actor.rpg.sprite/self — publish it, then re-check.') } finally { setBusy(null) } }
return ( <section className="w-full max-w-sm space-y-5"> <div className="space-y-1 text-center"> <h2 className="text-xs uppercase tracking-widest text-zinc-500">choose your actor</h2> <p className="text-sm text-zinc-400"> You need a sprite before you can play. Build your own, or start with the default actor. </p> </div>
<button onClick={useDefault} disabled={busy !== null} className="w-full rounded-md bg-zinc-100 px-3 py-2 text-sm font-medium text-zinc-900 hover:bg-white disabled:opacity-50" > {busy === 'default' ? 'setting up…' : 'Use the default actor'} </button>
<div className="flex items-center gap-3 text-[10px] uppercase tracking-widest text-zinc-600"> <span className="h-px flex-1 bg-zinc-800" /> or <span className="h-px flex-1 bg-zinc-800" /> </div>
<div className="space-y-3 rounded-md border border-zinc-800 bg-zinc-900 p-4"> <p className="text-sm text-zinc-300">Create your own at rpg.actor, then re-check.</p> <div className="flex items-center gap-4"> <a href="https://rpg.actor" target="_blank" rel="noreferrer" className="rounded bg-emerald-700 px-3 py-1.5 text-sm font-medium text-white hover:bg-emerald-600" > Create at rpg.actor ↗ </a> <button onClick={recheck} disabled={busy !== null} className="text-xs text-zinc-400 underline hover:text-zinc-200 disabled:opacity-50" > {busy === 'recheck' ? 'checking…' : 'I’ve created it — re-check'} </button> </div> </div>
{error && <p className="text-sm text-red-400">{error}</p>}
<div className="text-center"> <button onClick={onSignOut} className="text-xs text-zinc-600 underline hover:text-zinc-400"> log out </button> </div> </section> )}