From 9aee4f0e9bbff432707a5a7fd6e810a1bd15a786 Mon Sep 17 00:00:00 2001 From: kurihada Date: Fri, 27 Feb 2026 17:27:51 +0800 Subject: [PATCH] =?UTF-8?q?fix(blindbox):=20=E5=91=A8=E6=9C=AB=E5=A5=91?= =?UTF-8?q?=E7=BA=A6=E9=A1=B5=E8=BE=93=E5=85=A5=E6=A1=86=E6=97=A0=E6=B3=95?= =?UTF-8?q?=E8=BE=93=E5=85=A5=20=E2=80=94=20=E9=9D=9E=E5=8F=97=E6=8E=A7?= =?UTF-8?q?=E8=BE=93=E5=85=A5=20+=20=E5=85=A8=E5=B1=80=20input=20=E6=A0=B7?= =?UTF-8?q?=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 创建/加入房间输入改为 ref + defaultValue,提交时从 DOM 取值,避免受控 state 导致无法打字 - globals: input/textarea 增加 user-select: text !important、touch-action: manipulation - Input 组件: cursor-text、touch-manipulation、min-w-0、autoComplete=off --- src/app/blindbox/page.tsx | 84 ++++++++++++++++++++++++--------------- src/app/globals.css | 9 +++++ src/components/Input.tsx | 3 +- 3 files changed, 63 insertions(+), 33 deletions(-) diff --git a/src/app/blindbox/page.tsx b/src/app/blindbox/page.tsx index 6968e05..b0971f7 100644 --- a/src/app/blindbox/page.tsx +++ b/src/app/blindbox/page.tsx @@ -1,6 +1,6 @@ "use client"; -import { useState, useEffect, useCallback } from "react"; +import { useState, useEffect, useCallback, useRef } from "react"; import { useRouter } from "next/navigation"; import { motion, AnimatePresence } from "framer-motion"; import { @@ -38,9 +38,10 @@ export default function BlindboxLobbyPage() { const [rooms, setRooms] = useState([]); const [loading, setLoading] = useState(true); - const [createName, setCreateName] = useState(""); + const createNameRef = useRef(null); + const joinCodeRef = useRef(null); + const [joinCodeLength, setJoinCodeLength] = useState(0); const [creating, setCreating] = useState(false); - const [joinCode, setJoinCode] = useState(""); const [joining, setJoining] = useState(false); const [error, setError] = useState(""); const [loadError, setLoadError] = useState(false); @@ -64,20 +65,23 @@ export default function BlindboxLobbyPage() { return () => window.removeEventListener("nowhatever_auth", handler); }, []); - const fetchRooms = useCallback(async () => { + const fetchRooms = useCallback(async (silent = false) => { const p = getCachedProfile(); if (!p) return; - setLoading(true); - setLoadError(false); + if (!silent) { + setLoading(true); + setLoadError(false); + } try { const res = await fetch(`/api/blindbox/rooms?userId=${p.id}`); if (!res.ok) throw new Error(); const data = await res.json(); setRooms(Array.isArray(data.rooms) ? data.rooms : []); + setLoadError(false); } catch { - setLoadError(true); + if (!silent) setLoadError(true); } finally { - setLoading(false); + if (!silent) setLoading(false); } }, []); @@ -86,6 +90,17 @@ export default function BlindboxLobbyPage() { else setLoading(false); }, [loggedIn, fetchRooms]); + useEffect(() => { + if (!loggedIn) return; + const onFocus = () => fetchRooms(true); + window.addEventListener("focus", onFocus); + return () => window.removeEventListener("focus", onFocus); + }, [loggedIn, fetchRooms]); + + useEffect(() => { + if (rooms.length > 0) setJoinCodeLength(0); + }, [rooms.length]); + const handleAuth = (p: UserProfile) => { setProfile(p); setLoggedIn(true); @@ -94,13 +109,14 @@ export default function BlindboxLobbyPage() { const handleCreate = async () => { if (creating || !profile) return; + const name = createNameRef.current?.value?.trim() ?? ""; setCreating(true); setError(""); try { const res = await fetch("/api/blindbox/room", { method: "POST", headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ userId: profile.id, name: createName.trim() || undefined }), + body: JSON.stringify({ userId: profile.id, name: name || undefined }), }); const data = await res.json(); if (!res.ok) throw new Error(data.error); @@ -113,14 +129,15 @@ export default function BlindboxLobbyPage() { }; const handleJoin = async () => { - if (joining || !profile || !joinCode.trim()) return; + const code = joinCodeRef.current?.value?.toUpperCase().trim().slice(0, 6) ?? ""; + if (joining || !profile || code.length < 6) return; setJoining(true); setError(""); try { const res = await fetch("/api/blindbox/room/join", { method: "POST", headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ userId: profile.id, code: joinCode.trim() }), + body: JSON.stringify({ userId: profile.id, code }), }); const data = await res.json(); if (!res.ok) throw new Error(data.error); @@ -282,17 +299,15 @@ export default function BlindboxLobbyPage() { 创建第一个房间,邀请 TA 一起玩

- {/* Inline create form */} -
+ {/* Inline create form — z-10 + isolation so inputs are above any overlay and receive touch */} +
{ - setCreateName(e.target.value.slice(0, 30)); - setError(""); - }} + defaultValue="" + onInput={() => setError("")} onKeyDown={(e) => { if (e.key === "Enter") handleCreate(); }} maxLength={30} size="xl" @@ -319,11 +334,14 @@ export default function BlindboxLobbyPage() {
{ - setJoinCode(e.target.value.toUpperCase().slice(0, 6)); + defaultValue="" + onInput={(e) => { + const v = (e.target as HTMLInputElement).value.toUpperCase().slice(0, 6); + (e.target as HTMLInputElement).value = v; + setJoinCodeLength(v.length); setError(""); }} onKeyDown={(e) => { if (e.key === "Enter") handleJoin(); }} @@ -336,7 +354,7 @@ export default function BlindboxLobbyPage() { onClick={handleJoin} variant="secondary" size="lg" - disabled={joinCode.trim().length < 6} + disabled={joinCodeLength < 6} loading={joining} icon={} > @@ -364,16 +382,14 @@ export default function BlindboxLobbyPage() { animate={{ opacity: 1, y: 0 }} exit={{ opacity: 0, y: -20 }} > - {/* Create row */} +
{ - setCreateName(e.target.value.slice(0, 30)); - setError(""); - }} + defaultValue="" + onInput={() => setError("")} onKeyDown={(e) => { if (e.key === "Enter") handleCreate(); }} maxLength={30} size="lg" @@ -393,11 +409,14 @@ export default function BlindboxLobbyPage() { {/* Join row */}
{ - setJoinCode(e.target.value.toUpperCase().slice(0, 6)); + defaultValue="" + onInput={(e) => { + const v = (e.target as HTMLInputElement).value.toUpperCase().slice(0, 6); + (e.target as HTMLInputElement).value = v; + setJoinCodeLength(v.length); setError(""); }} onKeyDown={(e) => { if (e.key === "Enter") handleJoin(); }} @@ -409,7 +428,7 @@ export default function BlindboxLobbyPage() {
{/* Room list */}
diff --git a/src/app/globals.css b/src/app/globals.css index 3bd166d..28b75f5 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -69,6 +69,15 @@ body { -webkit-tap-highlight-color: transparent; } +/* Allow inputs and textareas to receive focus and accept input (body has user-select: none) */ +input, +textarea, +[contenteditable="true"] { + -webkit-user-select: text !important; + user-select: text !important; + touch-action: manipulation; /* prevent scroll container from capturing touch on iOS */ +} + .scrollbar-none { -ms-overflow-style: none; scrollbar-width: none; diff --git a/src/components/Input.tsx b/src/components/Input.tsx index 585cbb5..633679c 100644 --- a/src/components/Input.tsx +++ b/src/components/Input.tsx @@ -21,7 +21,8 @@ const Input = forwardRef( ({ size = "md", variant = "default", className = "", ...rest }, ref) => ( ),