Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Others] 토너먼트 브라켓 및 스와이퍼 로딩 컴포넌트 적용 #1203 #1227

Merged
Show file tree
Hide file tree
Changes from 38 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
e4cf748
[Fix] 진행중인 토너먼트 없을 시 '0' 렌더링 수정 #1173
Clearsu Dec 20, 2023
e6a3cb9
[Chore] 파일 경로 변경 #1173
Clearsu Dec 20, 2023
8cb65e1
[Feat] boucing dots UI 컴포넌트 추가 #1173
Clearsu Dec 20, 2023
2e7be20
[Feat] bouncing dots 컴포넌트 추가 #1173
Clearsu Dec 20, 2023
45fbf99
[Style] 패자 어두운 오버레이 적용 #1173
Clearsu Dec 20, 2023
f627514
Merge branch 'main' of github.com:42organization/42gg.client into oth…
Clearsu Dec 21, 2023
5ffde25
[Refactor] 사용하지 않는 Import 제거
Clearsu Dec 21, 2023
b85b359
[Feat] 경기 상태에 따른 bouncing dots UI 표시 #1173
Clearsu Dec 21, 2023
a67dca4
[Refactor] before, live 데이터를 분리하여 반환하도록 수정 #1204
Clearsu Dec 22, 2023
2bf417c
[Refactor] 로딩컴포넌트 대체 #1203
Clearsu Dec 22, 2023
28a69e6
[Refactor] loading spinner 컴포넌트 분리 및 fallbackText 추가 #1203
Clearsu Dec 22, 2023
d30758c
[Refactor] loading spinner 컴포넌트 분리 #1203
Clearsu Dec 22, 2023
9cdede9
[Chore] 컴포넌트 경로 변경 #1203
Clearsu Dec 22, 2023
b483430
[WIP] 리팩토링 중 #1203
Clearsu Dec 22, 2023
9c03732
[Docs] 주석 제거 #1203
Clearsu Dec 22, 2023
93be361
[Chore] 경로 변경 #1203
Clearsu Dec 22, 2023
2fe673f
[Refactor] loading spinner #1203
Clearsu Dec 22, 2023
d6b432f
Merge branch 'main' of github.com:42organization/42gg.client into 120…
Clearsu Dec 27, 2023
4b68c52
[Chore] 디렉토리 삭제 #1204
Clearsu Dec 27, 2023
f85847d
[Chore] 미사용 컴포넌트 제거 #1204
Clearsu Dec 27, 2023
7e6e925
[Refactor] 의존성배열 수정, 참가 상태 문자열 수정 #1204
Clearsu Dec 27, 2023
d6fcab8
[Refactor] 리턴 타입 명시 #1204
Clearsu Dec 27, 2023
9f7eb02
[Refactor] 예정&진행중인 토너먼트 data fetching 커스텀훅 적용 #1204
Clearsu Dec 27, 2023
f089a9d
[Style] noTournamentText 색상 및 폰트 사이즈 조정 #1204
Clearsu Dec 27, 2023
cca7478
[Style] gap 및 폰트 사이즈 수정 #1204
Clearsu Dec 27, 2023
7de7da0
Merge branch 'others/예정-진행중-토너먼트-데이터-fetching-리팩토링-#1024' of github.c…
Clearsu Dec 27, 2023
55c436e
[Refactor] 데이터 요청 로직 분리 #1203
Clearsu Jan 1, 2024
33c5605
[Refactor] 사용하지 않는 스타일 삭제 #1203
Clearsu Jan 1, 2024
74ccebf
[Style] fallback 문구 수정 #1203
Clearsu Jan 1, 2024
6ef6406
[Refactor] stale time 프롭 추가 및 fallback text 제거 #1203
Clearsu Jan 4, 2024
3b94dc8
[Feat] 로딩 컴포넌트 표시 #1203
Clearsu Jan 4, 2024
4a3e795
[Refactor] 쿼리 키 변경 시 쿼리 초기화 #1203
Clearsu Jan 4, 2024
e449c4f
[Refactor] 중복 로직 및 컴포넌트 대체 #1203
Clearsu Jan 4, 2024
34953d3
[Refactor] 의존성배열 추가 #1203
Clearsu Jan 4, 2024
f3595b9
[Refactor] 사용하지 않는 스타일 삭제 #1203
Clearsu Jan 4, 2024
c2c1475
[Feat] loading spinner wrapper 추가 #1203
Clearsu Jan 4, 2024
245f501
[Chore] 사용하지 않는 훅 삭제 #1203
Clearsu Jan 4, 2024
745930d
Merge branch 'main' into others/토너먼트-브라켓-및-스와이퍼-로딩-컴포넌트-적용-#1203
Clearsu Jan 4, 2024
5fe846c
Merge branch 'main' into others/토너먼트-브라켓-및-스와이퍼-로딩-컴포넌트-적용-#1203
Clearsu Jan 4, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions components/UI/LoadingSpinner.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import styles from 'styles/UI/LoadingSpinner.module.scss';

export default function LoadingSpinner() {
return <div className={styles.loadingAnimation} />;
}
61 changes: 15 additions & 46 deletions components/tournament-record/WinnerSwiper.tsx
Original file line number Diff line number Diff line change
@@ -1,53 +1,32 @@
import React, {
useMemo,
useCallback,
SetStateAction,
forwardRef,
Ref,
} from 'react';
import React, { useMemo, SetStateAction, forwardRef, Ref } from 'react';
import { InfiniteData } from 'react-query';
import { EffectCoverflow } from 'swiper/modules';
import { Swiper, SwiperSlide, SwiperClass, SwiperRef } from 'swiper/react';
import { TournamentData, TournamentInfo } from 'types/tournamentTypes';
import { instance } from 'utils/axios';
import { InfiniteScroll } from 'utils/infinityScroll';
import { Swiper, SwiperSlide, SwiperRef, SwiperClass } from 'swiper/react';
import { TournamentInfo, TournamentData } from 'types/tournamentTypes';
import styles from 'styles/tournament-record/WinnerSwiper.module.scss';
import 'swiper/css';
import 'swiper/css/effect-coverflow';
import WinnerProfileImage from './WinnerProfileImage';

interface WinnerSwiperProps {
tournamentData: InfiniteData<TournamentData> | undefined;
type: string;
size: number;
setTournamentInfo: React.Dispatch<SetStateAction<TournamentInfo | undefined>>;
setIsEmpty: React.Dispatch<SetStateAction<boolean>>;
onIndexChange: (swiper: SwiperClass) => void;
}

const WinnerSwiper = forwardRef(
(
{ type, size, setTournamentInfo, setIsEmpty }: WinnerSwiperProps,
{
tournamentData,
type,
size,
setTournamentInfo,
onIndexChange,
}: WinnerSwiperProps,
ref: Ref<SwiperRef> | undefined
) => {
const fetchTournamentData = useCallback(
async (page: number) => {
const res = await instance.get(
`/pingpong/tournaments?page=${page}&type=${type}&size=${size}&status=END`
);
if (res.data.totalPage === 0) {
setIsEmpty(true);
} else {
setIsEmpty(false);
}
return res.data;
},
[type, size, setIsEmpty]
);

const { data, hasNextPage, fetchNextPage } = InfiniteScroll<TournamentData>(
['tournamentData', type],
fetchTournamentData,
'JC01'
);

const coverflowEffect = useMemo(
() => ({
rotate: 35,
Expand All @@ -58,16 +37,6 @@ const WinnerSwiper = forwardRef(
[]
);

const indexChangeHandler = useCallback(
(swiper: SwiperClass) => {
const slidesLength = swiper.slides.length;
if (hasNextPage && swiper.activeIndex >= slidesLength - 3) {
fetchNextPage();
}
},
[hasNextPage, fetchNextPage]
);

return (
<Swiper
className={styles.swiper}
Expand All @@ -76,10 +45,10 @@ const WinnerSwiper = forwardRef(
centeredSlides={true}
coverflowEffect={coverflowEffect}
modules={[EffectCoverflow]}
onActiveIndexChange={indexChangeHandler}
onActiveIndexChange={onIndexChange}
ref={ref}
>
{data?.pages.map((page, pageIndex) => (
{tournamentData?.pages.map((page, pageIndex) => (
<React.Fragment key={pageIndex}>
{page.tournaments.length > 0 &&
page.tournaments.map((tournament, index) => (
Expand Down
2 changes: 1 addition & 1 deletion components/tournament-record/WinnerTournamentInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export default function WinnerTournamentInfo({
return (
<div className={styles.winnerInfoContainer}>
<p className={styles.userId}>
{tournamentInfo?.winnerIntraId ?? '아이디 정보가 없습니다.'}
{tournamentInfo?.winnerIntraId ?? 'IntraId'}
</p>
<p className={styles.gameInfo}>
{tournamentInfo?.title}{' '}
Expand Down
16 changes: 9 additions & 7 deletions components/tournament/TournamentBraket.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import React from 'react';
import { useSetRecoilState } from 'recoil';
import { clickedTournamentState } from 'utils/recoil/tournament';
import TournamentMatch from 'components/tournament/TournamentMatch';
import LoadingSpinner from 'components/UI/LoadingSpinner';

if (typeof window !== 'undefined' && typeof window.navigator !== 'undefined') {
import('@g-loot/react-tournament-brackets');
Expand All @@ -19,7 +20,7 @@ if (typeof window !== 'undefined' && typeof window.navigator !== 'undefined') {
const SingleEliminationBracket = dynamic<
React.ComponentProps<typeof StaticSingleEliminationBracket>
>(
() => {
async () => {
return import('@g-loot/react-tournament-brackets').then(
(mod) => mod.SingleEliminationBracket
);
Expand All @@ -28,7 +29,7 @@ const SingleEliminationBracket = dynamic<
);

const SVGViewer = dynamic<React.ComponentProps<typeof StaticSVGViewer>>(
() => {
async () => {
return import('@g-loot/react-tournament-brackets').then(
(mod) => mod.SVGViewer
);
Expand All @@ -47,16 +48,17 @@ export default function TournamentBraket({
}: TournamentBraketProps) {
const setHighLightUser = useSetRecoilState(clickedTournamentState);

if (singleEliminationBracketMatchs.length === 0)
return <h1 style={{ color: 'white' }}>Loading...</h1>;
const finalWidth = containerSize?.width; //Math.max(width - 50, 500);
const finalHeight = containerSize?.height; //Math.max(height - 100, 500);
if (singleEliminationBracketMatchs.length === 0) {
return <LoadingSpinner />;
}

return (
<SingleEliminationBracket
matches={singleEliminationBracketMatchs}
onPartyClick={(party: Participant, won: boolean) => {
if (party.name !== '') setHighLightUser(party.name);
if (party.name !== '') {
setHighLightUser(party.name);
}
}}
matchComponent={TournamentMatch}
options={{
Expand Down
2 changes: 1 addition & 1 deletion components/tournament/TournamentCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ export default function TournamentCard({
.catch((error) => {
setError('JJH2');
});
}, []);
}, [tournamentId, setError]);

const start = dateToKRLocaleTimeString(new Date(startTime));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,18 @@ import { instance } from 'utils/axios';
import { convertTournamentGamesToBracketMatchs } from 'utils/handleTournamentGame';
import { errorState } from 'utils/recoil/error';
import TournamentBraket from 'components/tournament/TournamentBraket';
import LoadingSpinner from 'components/UI/LoadingSpinner';
import useComponentSize from 'hooks/util/useComponentSize';
import styles from 'styles/tournament-record/UserTournamentBracket.module.scss';
import styles from 'styles/tournament/UserTournamentBracket.module.scss';

interface UserTournamentBracketProps {
tournamentId: number | undefined;
queryStaleTime: number;
}

export default function UserTournamentBraket({
tournamentId,
queryStaleTime,
}: UserTournamentBracketProps) {
const setError = useSetRecoilState(errorState);
const [ref, size] = useComponentSize<HTMLDivElement>();
Expand All @@ -34,7 +37,7 @@ export default function UserTournamentBraket({
() => fetchTournamentGames(),
{
enabled: !!tournamentId, // tournamentId가 undefined가 아닐 때만 작동하도록
staleTime: 86400000, // 하루
staleTime: queryStaleTime,
}
);

Expand All @@ -45,7 +48,7 @@ export default function UserTournamentBraket({
return (
<div ref={ref} className={styles.bracketContainer}>
{isLoading ? (
<div className={styles.loadingAnimation} />
<LoadingSpinner />
) : (
<TournamentBraket
singleEliminationBracketMatchs={bracketMatches}
Expand Down
75 changes: 58 additions & 17 deletions pages/tournament-record.tsx
Original file line number Diff line number Diff line change
@@ -1,41 +1,82 @@
import { useState, useRef, useEffect } from 'react';
import { SwiperRef } from 'swiper/react';
import { TournamentInfo } from 'types/tournamentTypes';
import { useState, useRef, useEffect, useCallback } from 'react';
import { SwiperClass, SwiperRef } from 'swiper/react';
import { TournamentInfo, TournamentData } from 'types/tournamentTypes';
import { instance } from 'utils/axios';
import { InfiniteScroll } from 'utils/infinityScroll';
import UserTournamentBracket from 'components/tournament/UserTournamentBracket';
import LeagueButtonGroup from 'components/tournament-record/LeagueButtonGroup';
import UserTournamentBracket from 'components/tournament-record/UserTournamentBracket';
import WinnerSwiper from 'components/tournament-record/WinnerSwiper';
import WinnerTournamentInfo from 'components/tournament-record/WinnerTournamentInfo';
import LoadingSpinner from 'components/UI/LoadingSpinner';
import styles from 'styles/tournament-record/TournamentRecord.module.scss';

export default function TournamentRecord() {
const [tournamentInfo, setTournamentInfo] = useState<TournamentInfo>();
const [selectedType, setSelectedType] = useState<string>('ROOKIE');
const [isEmpty, setIsEmpty] = useState<boolean>(false);
const swiperRef = useRef<SwiperRef>(null);

const fetchTournamentData = useCallback(
async (page: number) => {
const res = await instance.get(
`pingpong/tournaments?page=${page}&type=${selectedType}&size=5&status=END`
);
return res.data;
},
[selectedType]
);

const { data, hasNextPage, fetchNextPage, isLoading } =
InfiniteScroll<TournamentData>(
['tournamentData', selectedType],
fetchTournamentData,
'JC01'
);

const indexChangeHandler = useCallback(
(swiper: SwiperClass) => {
const slidesLength = swiper.slides.length;
if (hasNextPage && swiper.activeIndex >= slidesLength - 3) {
fetchNextPage();
}
},
[hasNextPage, fetchNextPage]
);

useEffect(() => {
if (swiperRef.current?.swiper) {
swiperRef.current?.swiper.slideTo(0, 0); // index, speed
swiperRef.current?.swiper.slideTo(0, 0); // 이동할 슬라이드 인덱스, 이동 속도
}
}, [selectedType]);

return (
<div className={styles.pageWrap}>
<h1 className={styles.title}>Hall of Fame</h1>
<LeagueButtonGroup onSelect={setSelectedType} />
<WinnerSwiper
type={selectedType}
size={5}
setTournamentInfo={setTournamentInfo}
ref={swiperRef}
setIsEmpty={setIsEmpty}
/>
{isEmpty ? (
<p>종료된 토너먼트가 없습니다!</p>
{isLoading ? (
<div className={styles.spinnerWrapper}>
<LoadingSpinner />
</div>
) : (
<>
<WinnerTournamentInfo tournamentInfo={tournamentInfo} />
<UserTournamentBracket tournamentId={tournamentInfo?.tournamentId} />
<WinnerSwiper
tournamentData={data}
type={selectedType}
size={5}
setTournamentInfo={setTournamentInfo}
onIndexChange={indexChangeHandler}
ref={swiperRef}
/>
{data?.pages[0].totalPage === 0 ? (
<p>종료된 토너먼트가 없습니다!</p>
) : (
<>
<WinnerTournamentInfo tournamentInfo={tournamentInfo} />
<UserTournamentBracket
tournamentId={tournamentInfo?.tournamentId}
queryStaleTime={1000 * 60 * 60}
/>
</>
)}
</>
)}
</div>
Expand Down
Loading