From 26656f1e01d68c1cd47c6e66deb9a1df021ff27c Mon Sep 17 00:00:00 2001 From: kurihada Date: Thu, 26 Feb 2026 14:18:32 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=8C=B9=E9=85=8D=E6=88=90=E5=8A=9F?= =?UTF-8?q?=E9=A1=B5=E5=BC=95=E5=AF=BC=E6=9C=AA=E6=B3=A8=E5=86=8C=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E6=B3=A8=E5=86=8C=EF=BC=8C=E4=BF=9D=E5=AD=98=E5=86=B3?= =?UTF-8?q?=E7=AD=96=E8=AE=B0=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 未注册用户在匹配成功页看到注册引导卡片 - 注册后自动保存本次决策记录,收藏按钮同步出现 - 将 isRegistered() 调用改为 registered 响应式状态 - 更新 ROADMAP 标记已完成 --- ROADMAP.md | 2 +- src/components/MatchResult.tsx | 54 ++++++++++++++++++++++++++++++---- 2 files changed, 49 insertions(+), 7 deletions(-) diff --git a/ROADMAP.md b/ROADMAP.md index 7b8d538..3b2197d 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -39,7 +39,7 @@ - `viewport-fit=cover` 适配刘海屏 ### 首次体验引导优化 -- 极速救场完成一轮后引导注册("注册保存记录") +- ~~极速救场完成一轮后引导注册("注册保存记录")~~(已完成,匹配成功页展示注册卡片,注册后自动保存记录) - 盲盒模式先展示 demo / 动画,让用户看到价值再引导注册 - 统一两个模式的登录体验(目前极速救场不需登录,盲盒必须登录) diff --git a/src/components/MatchResult.tsx b/src/components/MatchResult.tsx index 2d84eb3..4be47f0 100644 --- a/src/components/MatchResult.tsx +++ b/src/components/MatchResult.tsx @@ -20,11 +20,13 @@ import { Share2, Zap, Heart, + UserPlus, } from "lucide-react"; -import { Restaurant, MatchType, RunnerUp, SceneType } from "@/types"; +import { Restaurant, MatchType, RunnerUp, SceneType, UserProfile } from "@/types"; import { fireCelebration, playChime } from "@/lib/celebrate"; import { isRegistered } from "@/lib/userId"; import ShareCardModal from "@/components/ShareCardModal"; +import AuthModal from "@/components/AuthModal"; interface MatchResultProps { restaurant: Restaurant; @@ -193,6 +195,8 @@ export default function MatchResult({ const isUnanimous = matchType === "unanimous"; const [favorited, setFavorited] = useState(false); const [favLoading, setFavLoading] = useState(false); + const [registered, setRegistered] = useState(() => isRegistered()); + const [showAuth, setShowAuth] = useState(false); const showToast = useCallback((msg: string) => { setToast(msg); @@ -212,7 +216,7 @@ export default function MatchResult({ useEffect(() => { if (historySavedRef.current) return; - if (!isRegistered()) return; + if (!registered) return; if (matchType === "no_match") return; historySavedRef.current = true; @@ -227,14 +231,20 @@ export default function MatchResult({ participants: userCount, }), }).catch(() => {}); - }, [userId, roomId, restaurant, matchType, userCount]); + }, [registered, userId, roomId, restaurant, matchType, userCount]); const handleOpenShareCard = useCallback(() => { setShowShareCard(true); }, []); + const handleAuth = useCallback((profile: UserProfile) => { + setRegistered(true); + setShowAuth(false); + showToast(`欢迎,${profile.username}!记录已保存`); + }, [showToast]); + const handleFavorite = useCallback(async () => { - if (!isRegistered() || favorited || favLoading) return; + if (!registered || favorited || favLoading) return; setFavLoading(true); try { const res = await fetch("/api/user/favorite", { @@ -250,7 +260,7 @@ export default function MatchResult({ /* ignore */ } setFavLoading(false); - }, [userId, restaurant, favorited, favLoading, showToast]); + }, [registered, userId, restaurant, favorited, favLoading, showToast]); if (matchType === "no_match") { return ; @@ -344,7 +354,7 @@ export default function MatchResult({ animate={{ y: 0, opacity: 1 }} transition={{ type: "spring", stiffness: 180, damping: 18, delay: 0.5 }} > - {isRegistered() && ( + {registered && ( + {/* Registration nudge */} + {!registered && ( + +

+ 注册后,决策记录和收藏不会丢失 +

+

+ 仅需用户名 + 密码,10 秒完成 +

+ setShowAuth(true)} + className="mt-3 flex h-10 w-full items-center justify-center gap-2 rounded-xl bg-accent text-sm font-bold text-white shadow-lg shadow-accent/20 transition-colors hover:bg-accent-hover" + whileTap={{ scale: 0.95 }} + > + + 注册保存记录 + +
+ )} + {/* Runner ups */} {!isUnanimous && runnerUpRestaurants.length > 0 && ( + setShowAuth(false)} + onAuth={handleAuth} + defaultTab="register" + /> + setShowShareCard(false)}