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() {
}
>
@@ -426,6 +445,7 @@ export default function BlindboxLobbyPage() {
{error}
)}
+
{/* 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) => (
),