feat: 接入全站图片资源 + 修复卡片滑动与房间轮询问题
图片资源接入: - OG/Twitter 社交分享元数据 (og-image.png) - 错误页插画替换图标 (error-robot.png) - EmptyState 组件支持 image prop,空状态页面接入插画 - 餐厅图片 fallback 改用 restaurant-fallback.png - 极速救场/周末契约页面添加 hero 装饰图 - 分享卡片添加背景图层 (share-bg-*.png),通过 base64 预加载 - 更新 App 图标 (apple-touch-icon, icon-192/512) Bug 修复: - SwipeDeck: swipe action 从 "nope" 改为 "pass",匹配 API 预期 - SwipeDeck: 用 ref 读取 currentIndex 避免竞态重置(本地滑动后 被服务端旧 swipeCounts 立即清零) - SwipeDeck: 卡片 key 加入 isTop 标识,强制 remount 解决 framer-motion drag 手势在 isTop 切换时不重新初始化的问题 - SwipeableCard: initial 统一为背景位置,确保晋升为顶部卡片时 有一致的放大动画 - useRoomPolling: roomId 为空时跳过 SWR 和 EventSource - room page: joinRoom 前 guard roomId,消除退房时 404 - layout: 添加 metadataBase 消除 Next.js OG 图片警告
This commit is contained in:
@@ -193,6 +193,7 @@ export default function AchievementsPage() {
|
||||
) : decisions.length === 0 ? (
|
||||
<EmptyState
|
||||
icon={ClipboardList}
|
||||
image="/empty-no-record.png"
|
||||
title="还没有决策记录"
|
||||
subtitle="使用极速救场后会在这里记录"
|
||||
color="amber"
|
||||
@@ -254,6 +255,7 @@ export default function AchievementsPage() {
|
||||
) : contracts.length === 0 ? (
|
||||
<EmptyState
|
||||
icon={BarChart3}
|
||||
image="/empty-no-room.png"
|
||||
title="还没有契约记录"
|
||||
subtitle="完成或过期的契约会在这里显示"
|
||||
color="purple"
|
||||
|
||||
@@ -167,11 +167,20 @@ export default function BlindboxLobbyPage() {
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
<motion.img
|
||||
src="/blindbox-hero.png"
|
||||
alt="周末契约"
|
||||
className="mt-3 h-28 w-auto"
|
||||
initial={{ opacity: 0, scale: 0.9 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
transition={{ duration: 0.5, delay: 0.05 }}
|
||||
/>
|
||||
|
||||
<motion.p
|
||||
className="mt-2 max-w-xs text-center text-xs leading-relaxed text-muted"
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ duration: 0.5, delay: 0.05 }}
|
||||
transition={{ duration: 0.5, delay: 0.08 }}
|
||||
>
|
||||
平日蓄水,周末开奖。把所有"想做但一直没做"的事,交给命运来决定。
|
||||
</motion.p>
|
||||
|
||||
+6
-7
@@ -2,7 +2,7 @@
|
||||
|
||||
import { useEffect } from "react";
|
||||
import { motion } from "framer-motion";
|
||||
import { AlertTriangle, RotateCcw, Home } from "lucide-react";
|
||||
import { RotateCcw, Home } from "lucide-react";
|
||||
import Button from "@/components/Button";
|
||||
|
||||
export default function Error({
|
||||
@@ -23,14 +23,13 @@ export default function Error({
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
>
|
||||
<motion.div
|
||||
className="relative flex h-20 w-20 items-center justify-center"
|
||||
<motion.img
|
||||
src="/error-robot.png"
|
||||
alt="错误"
|
||||
className="h-28 w-28"
|
||||
animate={{ y: [0, -4, 0] }}
|
||||
transition={{ duration: 2.5, repeat: Infinity, ease: "easeInOut" }}
|
||||
>
|
||||
<div className="absolute inset-0 rounded-2xl bg-rose-500/15 blur-lg" />
|
||||
<AlertTriangle size={36} className="relative text-rose-400/80" strokeWidth={1.5} />
|
||||
</motion.div>
|
||||
/>
|
||||
|
||||
<h1 className="mt-6 text-xl font-bold text-heading">出了点问题</h1>
|
||||
<p className="mt-2 max-w-xs text-sm text-muted">
|
||||
|
||||
@@ -17,7 +17,7 @@ export default function GlobalError({
|
||||
<html lang="zh-CN">
|
||||
<body style={{ margin: 0, fontFamily: "system-ui, sans-serif", background: "#0a0a0a", color: "#e5e5e5" }}>
|
||||
<div style={{ display: "flex", minHeight: "100dvh", flexDirection: "column", alignItems: "center", justifyContent: "center", padding: "1.5rem" }}>
|
||||
<div style={{ fontSize: "3rem" }}>⚠️</div>
|
||||
<img src="/error-robot.png" alt="错误" style={{ width: 120, height: 120 }} />
|
||||
<h1 style={{ marginTop: "1.5rem", fontSize: "1.25rem", fontWeight: 700 }}>应用崩溃了</h1>
|
||||
<p style={{ marginTop: "0.5rem", fontSize: "0.875rem", color: "#a3a3a3", textAlign: "center" }}>
|
||||
发生了严重错误,请尝试刷新页面
|
||||
|
||||
@@ -12,9 +12,21 @@ const geistSans = Geist({
|
||||
});
|
||||
|
||||
export const metadata: Metadata = {
|
||||
metadataBase: new URL(process.env.NEXT_PUBLIC_BASE_URL || "https://nowhatever.app"),
|
||||
title: "NoWhatever — 别说随便",
|
||||
description: "像 Tinder 一样滑卡片,和朋友一起决定去哪吃!",
|
||||
referrer: "no-referrer",
|
||||
openGraph: {
|
||||
title: "NoWhatever — 别说随便",
|
||||
description: "像 Tinder 一样滑卡片,和朋友一起决定去哪吃!",
|
||||
images: [{ url: "/og-image.png", width: 1200, height: 630 }],
|
||||
},
|
||||
twitter: {
|
||||
card: "summary_large_image",
|
||||
title: "NoWhatever — 别说随便",
|
||||
description: "像 Tinder 一样滑卡片,和朋友一起决定去哪吃!",
|
||||
images: ["/og-image.png"],
|
||||
},
|
||||
};
|
||||
|
||||
export const viewport: Viewport = {
|
||||
|
||||
+10
-1
@@ -214,11 +214,20 @@ export default function PanicPage() {
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
<motion.img
|
||||
src="/panic-hero.png"
|
||||
alt="极速救场"
|
||||
className="mt-3 h-28 w-auto"
|
||||
initial={{ opacity: 0, scale: 0.9 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
transition={{ duration: 0.5, delay: 0.05 }}
|
||||
/>
|
||||
|
||||
<motion.p
|
||||
className="mt-2 max-w-xs text-center text-xs leading-relaxed text-muted"
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ duration: 0.5, delay: 0.05 }}
|
||||
transition={{ duration: 0.5, delay: 0.08 }}
|
||||
>
|
||||
{sceneConfig.subtitle}
|
||||
</motion.p>
|
||||
|
||||
@@ -31,6 +31,7 @@ export default function RoomPage() {
|
||||
} = useRoomPolling(roomId);
|
||||
|
||||
useEffect(() => {
|
||||
if (!roomId) return;
|
||||
const id = getUserId();
|
||||
setUserId(id);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user