feat: 匹配成功页引导未注册用户注册,保存决策记录
- 未注册用户在匹配成功页看到注册引导卡片 - 注册后自动保存本次决策记录,收藏按钮同步出现 - 将 isRegistered() 调用改为 registered 响应式状态 - 更新 ROADMAP 标记已完成
This commit is contained in:
+1
-1
@@ -39,7 +39,7 @@
|
||||
- `viewport-fit=cover` 适配刘海屏
|
||||
|
||||
### 首次体验引导优化
|
||||
- 极速救场完成一轮后引导注册("注册保存记录")
|
||||
- ~~极速救场完成一轮后引导注册("注册保存记录")~~(已完成,匹配成功页展示注册卡片,注册后自动保存记录)
|
||||
- 盲盒模式先展示 demo / 动画,让用户看到价值再引导注册
|
||||
- 统一两个模式的登录体验(目前极速救场不需登录,盲盒必须登录)
|
||||
|
||||
|
||||
@@ -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 <NoMatchResult onReset={onReset} resetting={resetting} />;
|
||||
@@ -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 && (
|
||||
<motion.button
|
||||
onClick={handleFavorite}
|
||||
disabled={favLoading}
|
||||
@@ -466,6 +476,31 @@ export default function MatchResult({
|
||||
</motion.button>
|
||||
</motion.div>
|
||||
|
||||
{/* Registration nudge */}
|
||||
{!registered && (
|
||||
<motion.div
|
||||
className="mt-5 w-full rounded-2xl bg-surface/80 p-4 ring-1 ring-border/50 backdrop-blur-sm"
|
||||
initial={{ y: 20, opacity: 0 }}
|
||||
animate={{ y: 0, opacity: 1 }}
|
||||
transition={{ delay: 0.75 }}
|
||||
>
|
||||
<p className="text-sm font-medium text-gray-300">
|
||||
注册后,决策记录和收藏不会丢失
|
||||
</p>
|
||||
<p className="mt-1 text-xs text-muted">
|
||||
仅需用户名 + 密码,10 秒完成
|
||||
</p>
|
||||
<motion.button
|
||||
onClick={() => 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 }}
|
||||
>
|
||||
<UserPlus size={15} />
|
||||
注册保存记录
|
||||
</motion.button>
|
||||
</motion.div>
|
||||
)}
|
||||
|
||||
{/* Runner ups */}
|
||||
{!isUnanimous && runnerUpRestaurants.length > 0 && (
|
||||
<motion.div
|
||||
@@ -557,6 +592,13 @@ export default function MatchResult({
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
<AuthModal
|
||||
open={showAuth}
|
||||
onClose={() => setShowAuth(false)}
|
||||
onAuth={handleAuth}
|
||||
defaultTab="register"
|
||||
/>
|
||||
|
||||
<ShareCardModal
|
||||
open={showShareCard}
|
||||
onClose={() => setShowShareCard(false)}
|
||||
|
||||
Reference in New Issue
Block a user