diff --git a/apps/web/src/components/NicknameForm.tsx b/apps/web/src/components/NicknameForm.tsx index 6dd56f5..529304e 100644 --- a/apps/web/src/components/NicknameForm.tsx +++ b/apps/web/src/components/NicknameForm.tsx @@ -4,7 +4,18 @@ import { nicknameError, normalizeNickname, suggestNickname } from '@bg/shared/li import { api, discordLoginUrl } from '../net/api'; import { useSession } from '../store/session'; -export function NicknameForm({ next, submitLabel = '이 이름으로 시작하기', onDone }: { next: string; submitLabel?: string; onDone?: () => void }) { +export function NicknameForm({ + next, + submitLabel = '이 이름으로 시작하기', + onDone, + showDiscord = true, +}: { + next: string; + submitLabel?: string; + onDone?: () => void; + /** The login page shows its own big Discord button, so it hides this one. */ + showDiscord?: boolean; +}) { const [nick, setNick] = useState(() => suggestNickname()); const [error, setError] = useState(null); const [busy, setBusy] = useState(false); @@ -49,7 +60,7 @@ export function NicknameForm({ next, submitLabel = '이 이름으로 시작하 - {discord && ( + {discord && showDiscord && ( 디스코드로 로그인 diff --git a/apps/web/src/main.tsx b/apps/web/src/main.tsx index 4bae2cc..c2f6c51 100644 --- a/apps/web/src/main.tsx +++ b/apps/web/src/main.tsx @@ -70,6 +70,7 @@ function Shell() { } /> } /> } /> + } /> } /> } /> } /> diff --git a/apps/web/src/pages/LoginPage.tsx b/apps/web/src/pages/LoginPage.tsx index a8cc6b2..277413c 100644 --- a/apps/web/src/pages/LoginPage.tsx +++ b/apps/web/src/pages/LoginPage.tsx @@ -1,7 +1,11 @@ -/** 로그인만 있는 페이지: 닉네임으로 시작 또는 디스코드 로그인. 설정은 로그인 뒤 헤더 계정 메뉴 → 설정(/me). */ -import { Navigate, useNavigate, useSearchParams } from 'react-router'; +/** + * 로그인 페이지: 큰 [디스코드로 로그인] + 아래 [게스트로 시작하기]. + * 게스트를 고르면 /login/guest에서 계정 이름(닉네임)을 정한다. 설정은 로그인 뒤 헤더 계정 메뉴 → 설정(/me). + */ +import { Link, Navigate, useMatch, useNavigate, useSearchParams } from 'react-router'; import { TopBar } from '../components/ui'; import { NicknameForm } from '../components/NicknameForm'; +import { discordLoginUrl } from '../net/api'; import { useSession } from '../store/session'; /** Only same-site paths, so ?next= can't send people to another site. */ @@ -10,15 +14,51 @@ const safeNext = (v: string | null) => (v && v.startsWith('/') && !v.startsWith( export default function LoginPage() { const nav = useNavigate(); const me = useSession((s) => s.me); - const next = safeNext(useSearchParams()[0].get('next')); + const discord = useSession((s) => s.discordEnabled); + const guest = useMatch('/login/guest') !== null; + const [params] = useSearchParams(); + const next = safeNext(params.get('next')); + const query = params.toString() ? `?${params.toString()}` : ''; if (me) return ; + + if (guest) { + return ( + <> + nav(`/login${query}`)} /> +
+
+

계정 이름 정하기

+

게임에서 다른 사람에게 보일 이름이에요.

+ nav(next, { replace: true })} /> +
+
+ + ); + } + return ( <> nav('/')} />
-
+

로그인

- nav(next, { replace: true })} /> + {discord && ( + <> + + 디스코드로 로그인 + +

+ 기록이 계정에 저장돼서 다른 기기에서도 이어서 할 수 있어요. +

+
+ 또는 +
+ + )} + + 게스트로 시작하기 + +

가입 없이 이름만 정하면 돼요. 기록은 이 기기에만 남아요.

diff --git a/apps/web/src/styles/global.css b/apps/web/src/styles/global.css index 2298598..449b3e4 100644 --- a/apps/web/src/styles/global.css +++ b/apps/web/src/styles/global.css @@ -1185,3 +1185,28 @@ button.header-user { color: var(--primary); border-bottom-color: var(--primary); } + +/* Login page (docs/07 §3.2): big Discord button, guest below */ +.btn-discord { + min-height: 68px; + font-size: 1.2rem; + background: #5865f2; + border-color: #5865f2; + color: #fff; +} +.btn-discord:hover { + background: #4752c4; +} +.login-divider { + display: flex; + align-items: center; + gap: 12px; + color: var(--text-sub); + font-size: 0.95rem; +} +.login-divider::before, +.login-divider::after { + content: ''; + flex: 1; + border-top: 1px solid var(--border); +} diff --git a/docs/07-ui-ux.md b/docs/07-ui-ux.md index a1d602c..4d2729a 100644 --- a/docs/07-ui-ux.md +++ b/docs/07-ui-ux.md @@ -57,7 +57,7 @@ └──────────────────────────────────────────────────────────────────────────┘ ``` - 어느 화면에서든 코드를 바로 넣어 참여할 수 있게 상단에 코드 입력칸을 둔다. -- 오른쪽 끝: 로그인 전에는 [로그인](→ `/login`, 닉네임으로 시작·디스코드 로그인만 있음). 로그인 뒤에는 닉네임(디스코드로 가입했으면 디스코드 이름)을 보이고, 누르면 [⚙️ 설정](→ `/me`)·[🚪 로그아웃] 메뉴가 열린다. 화면·소리 설정은 `/me`에만 있다. +- 오른쪽 끝: 로그인 전에는 [로그인](→ `/login`: 큰 [디스코드로 로그인], 아래 [게스트로 시작하기] → `/login/guest`에서 계정 이름 정하기). 로그인 뒤에는 닉네임(디스코드로 가입했으면 디스코드 이름)을 보이고, 누르면 [⚙️ 설정](→ `/me`)·[🚪 로그아웃] 메뉴가 열린다. 화면·소리 설정은 `/me`에만 있다. - 내용 폭은 최대 1280px, 가운데 정렬. 게임 화면만 화면 전체 폭을 쓴다. ### 3.3 홈 (PC) diff --git a/e2e/replay.e2e.ts b/e2e/replay.e2e.ts index a31359e..f070b28 100644 --- a/e2e/replay.e2e.ts +++ b/e2e/replay.e2e.ts @@ -182,6 +182,7 @@ try { const c = await (await browser.newContext({ locale: 'ko-KR' })).newPage(); await c.goto(`${ORIGIN}/`); await c.goto(`${ORIGIN}/me`); + await c.getByRole('link', { name: '게스트로 시작하기' }).click(); await c.getByLabel('내 이름(닉네임)').fill('구경꾼'); await c.getByRole('button', { name: /시작하기/ }).click(); await c.getByText('아직 다시 볼 게임이 없어요.').waitFor();