치지직 앱으로 연동하고, 연동 확인된 ID만 접속할 수 있도록 수정, 개발용으로는 채널ID를 수동으로 접속할 수 있도록 변경.
최초 추천 고정 배치 추가.
This commit is contained in:
@@ -6,6 +6,7 @@ import { useSearchParams } from 'next/navigation';
|
||||
import DicePlane from './diceplane';
|
||||
import { DEFAULT_ASSETS, getAssets } from './asset-storage';
|
||||
import { useRoom } from '@/lib/rooms/client';
|
||||
import { SHOW_DEBUG_UI } from '@/lib/ui/debug';
|
||||
|
||||
function GamePageInner() {
|
||||
const searchParams = useSearchParams();
|
||||
@@ -13,7 +14,10 @@ function GamePageInner() {
|
||||
const [backgroundSrc, setBackgroundSrc] = useState(DEFAULT_ASSETS.background);
|
||||
const [ready, setReady] = useState(false);
|
||||
const [simAmount, setSimAmount] = useState(1000);
|
||||
const { room, error, consumeDice, simulateDonation } = useRoom(roomId);
|
||||
const [settingsOpen, setSettingsOpen] = useState(false);
|
||||
const [addDiceCount, setAddDiceCount] = useState(1);
|
||||
const [addDiceHint, setAddDiceHint] = useState<string | null>(null);
|
||||
const { room, error, consumeDice, simulateDonation, addDice } = useRoom(roomId);
|
||||
const chzzkOn = Boolean(room?.settings.chzzkEnabled);
|
||||
const manualMode = Boolean(roomId) && room != null && !chzzkOn;
|
||||
|
||||
@@ -22,6 +26,21 @@ function GamePageInner() {
|
||||
setReady(true);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!settingsOpen) setAddDiceHint(null);
|
||||
}, [settingsOpen]);
|
||||
|
||||
const onAddDice = async () => {
|
||||
if (!roomId || !chzzkOn) return;
|
||||
const count = Math.max(1, Math.trunc(addDiceCount) || 1);
|
||||
const result = await addDice(count);
|
||||
if (!result.ok) {
|
||||
setAddDiceHint(result.error || '추가 실패');
|
||||
return;
|
||||
}
|
||||
setAddDiceHint(`주사위 +${count} 추가했습니다.`);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="relative h-screen w-screen overflow-hidden">
|
||||
{ready && (
|
||||
@@ -36,67 +55,63 @@ function GamePageInner() {
|
||||
)}
|
||||
<div className="absolute inset-0 z-0 bg-black/25" aria-hidden />
|
||||
|
||||
<div className="absolute top-3 left-3 z-40 flex flex-col items-start gap-2">
|
||||
<Link
|
||||
href="/"
|
||||
className="rounded bg-black/70 px-3 py-1.5 text-sm text-white hover:bg-black/85"
|
||||
>
|
||||
홈
|
||||
</Link>
|
||||
{roomId && chzzkOn && (
|
||||
<div className="max-w-[min(100vw-2rem,20rem)] rounded-xl bg-black/75 px-3 py-2 text-xs text-white">
|
||||
<p className="font-semibold">주사위 큐: {room?.diceQueue ?? '…'}</p>
|
||||
{room?.settings.channelId && (
|
||||
<p className="mt-1 break-all font-mono text-[10px] text-white/50">
|
||||
채널 ID: {room.settings.channelId}
|
||||
</p>
|
||||
)}
|
||||
{room && (
|
||||
<p className="mt-1 text-white/70">
|
||||
{room.channelName || room.settings.channelId || '—'} · {room.chatStatus}
|
||||
{` · ${room.settings.cheesePerDice}원/${
|
||||
room.settings.multiplyByCheese
|
||||
? `배수${room.settings.maxMultiplier > 0 ? `≤${room.settings.maxMultiplier}` : ''}`
|
||||
: '1회'
|
||||
}`}
|
||||
</p>
|
||||
)}
|
||||
{error && <p className="mt-1 text-red-300">{error}</p>}
|
||||
{room?.chatError && <p className="mt-1 text-red-300">{room.chatError}</p>}
|
||||
{room?.lastDonations?.[0] && (
|
||||
<p className="mt-1 text-amber-200">
|
||||
최근: {room.lastDonations[0].donatorNickname}{' '}
|
||||
{room.lastDonations[0].payAmount}원 → +{room.lastDonations[0].diceAdded}
|
||||
</p>
|
||||
)}
|
||||
<div className="mt-2 flex items-center gap-1">
|
||||
<input
|
||||
type="number"
|
||||
min={1}
|
||||
value={simAmount}
|
||||
onChange={(e) => setSimAmount(Number(e.target.value) || 1)}
|
||||
className="w-24 rounded border border-white/20 bg-black/50 px-2 py-1 text-white"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
className="rounded bg-white/15 px-2 py-1 hover:bg-white/25"
|
||||
onClick={() => void simulateDonation(simAmount).catch(() => undefined)}
|
||||
>
|
||||
후원 시뮬
|
||||
</button>
|
||||
{SHOW_DEBUG_UI && (
|
||||
<div className="absolute top-3 left-3 z-40 flex flex-col items-start gap-2">
|
||||
{roomId && chzzkOn && (
|
||||
<div className="max-w-[min(100vw-2rem,20rem)] rounded-xl bg-black/75 px-3 py-2 text-xs text-white">
|
||||
<p className="font-semibold">주사위 큐: {room?.diceQueue ?? '…'}</p>
|
||||
{room?.settings.channelId && (
|
||||
<p className="mt-1 break-all font-mono text-[10px] text-white/50">
|
||||
채널 ID: {room.settings.channelId}
|
||||
</p>
|
||||
)}
|
||||
{room && (
|
||||
<p className="mt-1 text-white/70">
|
||||
{room.channelName || room.settings.channelId || '—'} · {room.chatStatus}
|
||||
{` · ${room.settings.cheesePerDice}원/${
|
||||
room.settings.multiplyByCheese
|
||||
? `배수${room.settings.maxMultiplier > 0 ? `≤${room.settings.maxMultiplier}` : ''}`
|
||||
: '1회'
|
||||
}`}
|
||||
</p>
|
||||
)}
|
||||
{error && <p className="mt-1 text-red-300">{error}</p>}
|
||||
{room?.chatError && <p className="mt-1 text-red-300">{room.chatError}</p>}
|
||||
{room?.lastDonations?.[0] && (
|
||||
<p className="mt-1 text-amber-200">
|
||||
최근: {room.lastDonations[0].donatorNickname}{' '}
|
||||
{room.lastDonations[0].payAmount}원 → +{room.lastDonations[0].diceAdded}
|
||||
</p>
|
||||
)}
|
||||
<div className="mt-2 flex items-center gap-1">
|
||||
<input
|
||||
type="number"
|
||||
min={1}
|
||||
value={simAmount}
|
||||
onChange={(e) => setSimAmount(Number(e.target.value) || 1)}
|
||||
className="w-24 rounded border border-white/20 bg-black/50 px-2 py-1 text-white"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
className="rounded bg-white/15 px-2 py-1 hover:bg-white/25"
|
||||
onClick={() => void simulateDonation(simAmount).catch(() => undefined)}
|
||||
>
|
||||
후원 시뮬
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{manualMode && (
|
||||
<div className="max-w-[min(100vw-2rem,20rem)] rounded-xl bg-black/75 px-3 py-2 text-xs text-white">
|
||||
<p className="font-semibold">수동 모드</p>
|
||||
<p className="mt-1 text-white/70">
|
||||
치지직·채팅 연동 없음 · 주사위는 자유롭게 굴릴 수 있습니다.
|
||||
</p>
|
||||
{error && <p className="mt-1 text-red-300">{error}</p>}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{manualMode && (
|
||||
<div className="max-w-[min(100vw-2rem,20rem)] rounded-xl bg-black/75 px-3 py-2 text-xs text-white">
|
||||
<p className="font-semibold">수동 모드</p>
|
||||
<p className="mt-1 text-white/70">
|
||||
치지직·채팅 연동 없음 · 주사위는 자유롭게 굴릴 수 있습니다.
|
||||
</p>
|
||||
{error && <p className="mt-1 text-red-300">{error}</p>}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="relative z-10 h-full w-full">
|
||||
<DicePlane
|
||||
@@ -105,8 +120,88 @@ function GamePageInner() {
|
||||
onConsumeDice={consumeDice}
|
||||
showChatFeed={chzzkOn}
|
||||
chatFeed={chzzkOn ? room?.chatFeed ?? [] : []}
|
||||
settingsButton={
|
||||
<button
|
||||
type="button"
|
||||
aria-label="설정"
|
||||
aria-expanded={settingsOpen}
|
||||
onClick={() => setSettingsOpen((v) => !v)}
|
||||
className="rounded-md bg-black/70 px-3 py-1.5 text-sm font-semibold text-white shadow-md hover:bg-black/85"
|
||||
>
|
||||
설정
|
||||
</button>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{settingsOpen && (
|
||||
<div
|
||||
className="absolute inset-0 z-50 flex items-center justify-center bg-black/45 p-4"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-label="게임 설정"
|
||||
onClick={() => setSettingsOpen(false)}
|
||||
>
|
||||
<div
|
||||
className="w-full max-w-sm rounded-2xl border border-white/15 bg-slate-950/95 px-5 py-4 text-white shadow-2xl"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
<h2 className="text-base font-semibold">설정</h2>
|
||||
<button
|
||||
type="button"
|
||||
className="rounded px-2 py-1 text-sm text-white/70 hover:bg-white/10 hover:text-white"
|
||||
onClick={() => setSettingsOpen(false)}
|
||||
>
|
||||
닫기
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="mt-4 flex flex-col gap-3">
|
||||
<Link
|
||||
href="/"
|
||||
className="rounded-lg bg-white/10 px-4 py-2.5 text-center text-sm font-medium hover:bg-white/15"
|
||||
>
|
||||
홈으로
|
||||
</Link>
|
||||
|
||||
{roomId && chzzkOn ? (
|
||||
<div className="rounded-lg border border-white/10 bg-black/30 p-3">
|
||||
<p className="text-xs text-white/60">주사위 큐 수동 추가</p>
|
||||
<p className="mt-1 text-sm text-white/80">
|
||||
현재 큐: <span className="font-semibold">{room?.diceQueue ?? 0}</span>
|
||||
</p>
|
||||
<div className="mt-2 flex items-center gap-2">
|
||||
<input
|
||||
type="number"
|
||||
min={1}
|
||||
value={addDiceCount}
|
||||
onChange={(e) => setAddDiceCount(Number(e.target.value) || 1)}
|
||||
className="w-20 rounded border border-white/20 bg-black/50 px-2 py-1.5 text-sm text-white"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
className="rounded-lg bg-sky-700 px-3 py-1.5 text-sm font-medium hover:bg-sky-600"
|
||||
onClick={() => void onAddDice()}
|
||||
>
|
||||
카운트 추가
|
||||
</button>
|
||||
</div>
|
||||
{addDiceHint && (
|
||||
<p className="mt-2 text-xs text-amber-200">{addDiceHint}</p>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<p className="rounded-lg border border-white/10 bg-black/30 px-3 py-2 text-xs text-white/65">
|
||||
{manualMode
|
||||
? '수동 모드에서는 주사위 큐 없이 바로 굴릴 수 있습니다.'
|
||||
: '치지직 연동 방에 입장하면 여기서 주사위 카운트를 추가할 수 있습니다.'}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user