refactor(P1): 5 项代码质量改进 — 消除重复、拆分巨型组件、统一基础设施
Task 4: 统一 amap.ts 为完整 API 客户端 - 扩展 amap.ts 为统一客户端(amapFetch 8s 超时 + 错误处理) - 导出 searchPlaceText/searchPlaceAround/getInputTips/reverseGeocode/getTransitDirection - 精简 4 个 location route 为单行调用,blindboxPlanGen 删除 ~80 行内联 API 代码 Task 2: 抽取 ShareCardShell 消除三兄弟重复 - 新建 ShareCardShell.tsx 共享外框/背景/品牌头/QR 底部 - RestaurantShareCard 406→268 行,BlindboxShareCard 341→173 行,BlindboxPlanShareCard 277→159 行 Task 3: 拆分 BlindboxPlan.tsx (742→371 行) - 提取 planUtils.ts (guessCategory + formatDuration) - 提取 PoiSearchField / SortablePlanItem / PlanItemEditModal 三个独立组件 Task 1: 拆分 blindbox/[code]/page.tsx 上帝组件 (1300→509 行) - 提取 useBlindboxRoom / useBlindboxIdeas / useBlindboxPlan / useBlindboxDraw 四个 hooks - 提取 BlindboxPoolPhase / BlindboxRevealPhase 两个子组件 - 主页面仅保留 phase 协调 + hook 组装 + 子组件渲染 Task 5: 统一 SWR 数据获取层 - 新建 fetcher.ts (FetchError 携带 status,401 不重试) - 新建 useBlindboxRooms / useAchievements / useFavorites SWR hooks - useRoomPolling 改用共享 fetcher - blindbox 大厅/成就/个人中心页面删除手写 fetch 样板代码 - JWT 过期时自动弹出登录框而非反复重试
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useEffect, useCallback } from "react";
|
||||
import { useState, useEffect } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { motion, AnimatePresence } from "framer-motion";
|
||||
import {
|
||||
@@ -22,10 +22,11 @@ import Card from "@/components/Card";
|
||||
import Input from "@/components/Input";
|
||||
import ProfileFavoritesCard from "@/components/ProfileFavoritesCard";
|
||||
import { useToast } from "@/hooks/useToast";
|
||||
import { useFavorites } from "@/hooks/useFavorites";
|
||||
import { ProfileCardSkeleton, RecordItemSkeleton } from "@/components/Skeleton";
|
||||
import { getUserId, getCachedProfile, setCachedProfile, setCachedPreferences, logout } from "@/lib/userId";
|
||||
import { getAvatarBg, AVATARS } from "@/lib/avatars";
|
||||
import type { UserProfile, UserPreferences, FavoriteRecord } from "@/types";
|
||||
import type { UserProfile, UserPreferences } from "@/types";
|
||||
|
||||
export default function ProfilePage() {
|
||||
const router = useRouter();
|
||||
@@ -33,8 +34,7 @@ export default function ProfilePage() {
|
||||
const [profile, setProfile] = useState<(UserProfile & { email?: string; preferences?: UserPreferences; decisionCount?: number }) | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
const [favorites, setFavorites] = useState<FavoriteRecord[]>([]);
|
||||
const [favLoading, setFavLoading] = useState(false);
|
||||
const { favorites, isLoading: favLoading, mutate: mutateFavorites } = useFavorites(userId || undefined);
|
||||
|
||||
const [editingUsername, setEditingUsername] = useState(false);
|
||||
const [newUsername, setNewUsername] = useState("");
|
||||
@@ -87,17 +87,6 @@ export default function ProfilePage() {
|
||||
.finally(() => setLoading(false));
|
||||
}, [router]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!userId) return;
|
||||
|
||||
setFavLoading(true);
|
||||
fetch(`/api/user/favorite?userId=${userId}`)
|
||||
.then((r) => { if (!r.ok) throw new Error(); return r.json(); })
|
||||
.then((data) => setFavorites(Array.isArray(data) ? data : []))
|
||||
.catch((e) => { console.error("ProfilePage: fetch favorites failed:", e); })
|
||||
.finally(() => setFavLoading(false));
|
||||
}, [userId]);
|
||||
|
||||
const handleSaveUsername = async () => {
|
||||
const trimmed = newUsername.trim();
|
||||
if (trimmed.length < 2 || trimmed.length > 16) {
|
||||
@@ -220,7 +209,7 @@ export default function ProfilePage() {
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ userId, favoriteId: favId }),
|
||||
});
|
||||
setFavorites((f) => f.filter((x) => x.id !== favId));
|
||||
mutateFavorites((prev) => prev?.filter((x) => x.id !== favId), false);
|
||||
toast.show("已取消收藏");
|
||||
} catch {
|
||||
toast.show("操作失败");
|
||||
|
||||
Reference in New Issue
Block a user