From 86c3b6651b681a7603dba6546a27d61aaeb7a961 Mon Sep 17 00:00:00 2001 From: kyuuseiryuu Date: Mon, 16 Mar 2026 19:33:44 +0900 Subject: [PATCH] feat(fav-players): optimize un-fav API response and refactor player list UI - Modify the DELETE /api/fav endpoint to include the player uid in the response JSON. - Refactor FavPlayersPage to remove explicit dependency on `aud` for API calls, relying solely on `useAuthHeaders`. - Add a dedicated "un-fav" button to player cards to allow users to unfollow individual players. - Implement logic to distinguish between local un-fav (for unauthenticated users) and server-side un-fav (for authenticated users). - Improve UI layout using `styled-components` and updated Ant Design components (`Typography.Title`). - Add a link to sign in and view cloud favorites when the local list is empty. --- src/index.tsx | 2 +- src/page/FavPlayersPage.tsx | 62 +++++++++++++++++++++++++------------ 2 files changed, 43 insertions(+), 21 deletions(-) diff --git a/src/index.tsx b/src/index.tsx index c36a5f7..091360b 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -172,7 +172,7 @@ const server = serve({ async DELETE(req) { const payload = await verifyLogtoToken(req.headers); await unFavPlayer(`${payload.sub}`, req.params.uid); - return Response.json({ ok: 'ok' }); + return Response.json({ ok: 'ok' , uid: req.params.uid }); } }, '/api/battle/:eventId': { diff --git a/src/page/FavPlayersPage.tsx b/src/page/FavPlayersPage.tsx index 50b0352..00737f6 100644 --- a/src/page/FavPlayersPage.tsx +++ b/src/page/FavPlayersPage.tsx @@ -1,13 +1,14 @@ import { Avatar, Button, Card, Divider, Flex, Image, message as AntdMessage, Popconfirm, Radio, Segmented, Spin, Typography, App } from "antd"; -import { useFavPlayerStore } from "../store/useFavPlayerStore"; +import { useFavPlayerStore, type FavPlayer } from "../store/useFavPlayerStore"; import { useNavigate } from "react-router"; import { useCallback, useEffect, useMemo, useState } from "react"; import { useLogto, type IdTokenClaims } from "@logto/react"; -import { DeleteOutlined, LoginOutlined, SyncOutlined, UploadOutlined } from "@ant-design/icons"; +import { DeleteOutlined, LoginOutlined, ShopTwoTone, StarFilled, SyncOutlined, UploadOutlined } from "@ant-design/icons"; import { useRequest } from "ahooks"; import type { XCXProfile } from "../types"; import useAutoLogin from "../hooks/useAutoLogin"; import { useAuthHeaders } from "../hooks/useAuthHeaders"; +import styled from "styled-components"; enum SortType { DEFAULT = '注册时间', @@ -19,15 +20,22 @@ enum ShowType { ACCOUNT = '账号收藏', } +const StyledContainer = styled.div` + .player-name { + margin: 0; + } + .unfav-btn { + height: 100%; + } +`; export function FavePlayersPage() { const { favMap, unFav } = useFavPlayerStore(state => state); const [sortType, setSortType] = useState(SortType.DEFAULT); const [showType, setShowType] = useState(ShowType.LOCAL); - const [claims, setClaims] = useState(); const headers = useAuthHeaders(); const { autoSignIn } = useAutoLogin(); const localList = Object.values(favMap); - const favListRequest = useRequest(async (aud: string) => { + const favListRequest = useRequest(async () => { const res = await fetch(`/api/fav`, { headers }); const data = await res.json(); return data; @@ -52,29 +60,34 @@ export function FavePlayersPage() { useEffect(() => { if (!isAuthenticated) return; const id = setTimeout(async () => { - const claims = await getIdTokenClaims(); - setClaims(claims); - favListRequest.runAsync(claims?.aud!); + favListRequest.runAsync(); }, 300); return () => clearTimeout(id); }, [isAuthenticated, getIdTokenClaims]); const handleSyncFav = useCallback(async () => { if (!isAuthenticated) return; - const claims = await getIdTokenClaims()!; - const aud = claims?.aud; const jobs = list.map(async u => { await fetch(`/api/fav/${u.uid}`, { method: 'PUT', headers }); }); message.open({ key: 'sync', content: '同步中...', type: 'loading' }); await Promise.all(jobs); - await favListRequest.runAsync(aud!); + await favListRequest.runAsync(); message.open({ key: 'sync', content: '已同步', type: 'success' }); }, [isAuthenticated, list]); const handleClearLocal = useCallback(() => { list.forEach(e => unFav(e.uid)); }, []); + const handleUnFav = useCallback(async (e: FavPlayer) => { + if (showType === ShowType.LOCAL) { + unFav(e.uid); + } + if (showType === ShowType.ACCOUNT) { + await fetch(`/api/fav/${e.uid}`, { method: 'DELETE', headers }); + await favListRequest.runAsync(); + } + }, [showType, favListRequest, headers]); return ( -
+ 收藏的球员 @@ -88,7 +101,7 @@ export function FavePlayersPage() { ]} /> + )} ) : ( <> {list.map(e => ( - {e.avatar?.includes('noavatar') - ? {e.name} - : } + + {e.avatar?.includes('noavatar') + ? {e.name} + : } + navigate(`/profile/${e.uid}`)} > -

{e.name}

+ {e.name} {e.realname}
+ + +
))} @@ -162,6 +184,6 @@ export function FavePlayersPage() { )}
-
+ ); -} \ No newline at end of file +}