diff --git a/src/api/player..ts b/src/api/player..ts new file mode 100644 index 0000000..ae5d94d --- /dev/null +++ b/src/api/player..ts @@ -0,0 +1,39 @@ +import instance from '.'; + +type UserInfoType = { + accessId: string; + level: number; + nickname: string; +}; + +type UserRankInfoType = { + matchType: number; + division: number; + achievementDate: string; +}; + +export type FconlineInfoType = UserInfoType & UserRankInfoType; + +export const getFconlinePlayerInfo = async ( + nickname: string, +): Promise => { + const { data: userInfo } = await instance.get( + `https://public.api.nexon.com/openapi/fconline/v1.0/users?nickname=${nickname}`, + { + headers: { + Authorization: process.env.NEXT_PUBLIC_FCONLINE_KEY, + }, + }, + ); + + const { data: rankInfo } = await instance.get( + `https://public.api.nexon.com/openapi/fconline/v1.0/users/${userInfo.accessId}/maxdivision`, + { + headers: { + Authorization: process.env.NEXT_PUBLIC_FCONLINE_KEY, + }, + }, + ); + + return { ...userInfo, ...(rankInfo.pop() as UserRankInfoType) }; +}; diff --git a/src/app/match/[id]/page.tsx b/src/app/match/[id]/page.tsx index 367ffff..f6d0d7c 100644 --- a/src/app/match/[id]/page.tsx +++ b/src/app/match/[id]/page.tsx @@ -4,6 +4,7 @@ import { useRef, useState } from 'react'; import AsyncBoundary from '@/components/common/AsyncBoundary'; import Loader from '@/components/common/Loader'; +import FconlineUserLineup from '@/components/fcOnline/UserInfo'; import MatchBanner from '@/components/match/Banner'; import Cheer from '@/components/match/Cheer'; import CommentForm from '@/components/match/CommentForm'; @@ -13,10 +14,10 @@ import Panel from '@/components/match/Panel'; import RecordList from '@/components/match/RecordList'; import Video from '@/components/match/Video'; import useSocket from '@/hooks/useSocket'; +import FconlineLineupFetcher from '@/queries/useFconlineLineupById/Fetcher'; import MatchByIdFetcher from '@/queries/useMatchById/Fetcher'; import MatchCheerByIdFetcher from '@/queries/useMatchCheerById/Fetcher'; import MatchCommentFetcher from '@/queries/useMatchCommentById/Fetcher'; -import MatchLineupFetcher from '@/queries/useMatchLineupById/Fetcher'; import MatchTimelineFetcher from '@/queries/useMatchTimelineById/Fetcher'; import MatchVideoFetcher from '@/queries/useMatchVideoById/Fetcher'; import useSaveCommentMutation from '@/queries/useSaveCommentMutation/query'; @@ -86,14 +87,15 @@ export default function Match({ params }: { params: { id: string } }) { errorFallback={props => } loadingFallback={} > - - {([firstTeam, secondTeam]) => ( -
- - -
+ + {({ mergedUserInfo }) => ( + + //
+ // + // + //
)} -
+ )} {selected === '타임라인' && ( diff --git a/src/components/fcOnline/UserInfo/index.tsx b/src/components/fcOnline/UserInfo/index.tsx new file mode 100644 index 0000000..d4ee701 --- /dev/null +++ b/src/components/fcOnline/UserInfo/index.tsx @@ -0,0 +1,42 @@ +import { divisionMap } from '@/constants/divisionRank'; +import { FconlineLineupType } from '@/queries/useFconlineLineupById/Fetcher'; + +export default function FconlineUserLineup({ + userInfos, +}: { + userInfos: FconlineLineupType[]; +}) { + return ( +
+ {userInfos.map(info => ( +
+
+ {info.teamName} + 선수 👊 +
+
+ + NICKNAME + +
{info.nickname}
+
+
+ + LEVEL + +
LV. {info.level}
+
+
+ + RANK + +
{divisionMap.get(info.division)}
+
+
+ ))} +
+ ); +} diff --git a/src/constants/divisionRank.ts b/src/constants/divisionRank.ts new file mode 100644 index 0000000..e7f94ed --- /dev/null +++ b/src/constants/divisionRank.ts @@ -0,0 +1,79 @@ +export const divisionMap = new Map(); +const divisions = [ + { + divisionId: 800, + divisionName: '슈퍼챔피언스', + }, + { + divisionId: 900, + divisionName: '챔피언스', + }, + { + divisionId: 1000, + divisionName: '슈퍼챌린지', + }, + { + divisionId: 1100, + divisionName: '챌린지1', + }, + { + divisionId: 1200, + divisionName: '챌린지2', + }, + { + divisionId: 1300, + divisionName: '챌린지3', + }, + { + divisionId: 2000, + divisionName: '월드클래스1', + }, + { + divisionId: 2100, + divisionName: '월드클래스2', + }, + { + divisionId: 2200, + divisionName: '월드클래스3', + }, + { + divisionId: 2300, + divisionName: '프로1', + }, + { + divisionId: 2400, + divisionName: '프로2', + }, + { + divisionId: 2500, + divisionName: '프로3', + }, + { + divisionId: 2600, + divisionName: '세미프로1', + }, + { + divisionId: 2700, + divisionName: '세미프로2', + }, + { + divisionId: 2800, + divisionName: '세미프로3', + }, + { + divisionId: 2900, + divisionName: '유망주1', + }, + { + divisionId: 3000, + divisionName: '유망주2', + }, + { + divisionId: 3100, + divisionName: '유망주3', + }, +]; + +divisions.map(division => { + divisionMap.set(division.divisionId, division.divisionName); +}); diff --git a/src/queries/useFconlineLineupById/Fetcher.tsx b/src/queries/useFconlineLineupById/Fetcher.tsx new file mode 100644 index 0000000..3defa74 --- /dev/null +++ b/src/queries/useFconlineLineupById/Fetcher.tsx @@ -0,0 +1,35 @@ +import { ReactNode } from 'react'; + +import { FconlineInfoType } from '@/api/player.'; +import { MatchLineupType } from '@/types/match'; + +import { useMatchFconlineLineupById } from './query'; + +export type FconlineLineupType = MatchLineupType & FconlineInfoType; + +type FconlineLineupFetcherProps = { + matchId: string; + children: ({ + mergedUserInfo, + }: { + mergedUserInfo: FconlineLineupType[]; + }) => ReactNode; +}; + +export default function FconlineLineupFetcher({ + matchId, + children, +}: FconlineLineupFetcherProps) { + const { fconlineInfo, lineup, fconlineError, error } = + useMatchFconlineLineupById(matchId); + + const mergedUserInfo = fconlineInfo.map((info, index) => ({ + ...info, + ...lineup[index], + })); + + if (error) throw error; + if (fconlineError) throw fconlineError; + + return children({ mergedUserInfo }); +} diff --git a/src/queries/useFconlineLineupById/query.ts b/src/queries/useFconlineLineupById/query.ts new file mode 100644 index 0000000..ce35e04 --- /dev/null +++ b/src/queries/useFconlineLineupById/query.ts @@ -0,0 +1,39 @@ +import { useSuspenseQueries, useSuspenseQuery } from '@tanstack/react-query'; + +import { getMatchLineupById } from '@/api/match'; +import { FconlineInfoType, getFconlinePlayerInfo } from '@/api/player.'; + +export const useMatchFconlineLineupById = (matchId: string) => { + const { data, error } = useSuspenseQuery({ + queryKey: ['match-lineup', matchId], + queryFn: () => getMatchLineupById(matchId), + }); + + const nicknames = data + .map(info => ({ + order: info.order, + nickname: + info.gameTeamPlayers.map(player => player.description).pop() || '', + })) + .flat(); + + const queryOptions = nicknames.map(nickname => ({ + queryKey: ['fconline-lineup', nickname.nickname], + queryFn: () => getFconlinePlayerInfo(nickname.nickname), + select: (data: FconlineInfoType) => ({ ...data, order: nickname.order }), + })); + + const datas = useSuspenseQueries({ + queries: queryOptions, + }); + + const fconlineError = datas.find(data => data.error); + const fconlineInfo = datas.map(data => data.data); + + return { + lineup: data, + fconlineInfo, + fconlineError, + error, + }; +}; diff --git a/src/types/match.ts b/src/types/match.ts index 9b2d8b6..347d305 100644 --- a/src/types/match.ts +++ b/src/types/match.ts @@ -40,6 +40,7 @@ export type MatchLineupType = { gameTeamId: number; teamName: string; gameTeamPlayers: MatchPlayerType[]; + order: number; }; export type MatchPlayerType = {