Skip to content

Commit b2be48d

Browse files
authored
feat: 밸런스게임 모바일 페이지 수정, 삭제 기능 연결 (#306)
* refactor: 밸런스게임 모바일 조회 페이지의 회원 정보 함수 수정 * refactor: 게임 생성 페이지의 이미지 삭제 함수 내에서 fileId가 undefined일 경우 null이 지정되도록 수정 * feat: 게임 수정 클릭 시 게임 생성 페이지로 게임 값을 state로 넘겨주도록 구현 * feat: 게임 값과 게임 세트 아이디를 함께 넘겨주어 수정 로직 연결 * refactor: 비회원 상태를 로컬 스토리지가 아닌 리덕스로 확인하도록 수정 * feat: 게임 삭제 로직 연결 * feat: 밸런스게임 수정 페이지가 모바일이 아닐때만 접근되도록 구현 * feat: 이미지 업로드 응답 데이터에 타입 지정 * refactor: 게임 수정 시 이미지 삭제 로직 수정 * refactor: 게임의 서브 태그가 여러 개 입력이 가능하도록 로직 수정 * refactor: ToastModal 컴포넌트에 모바일 스타일 추가 * refactor: 게임 서브 태그 개수 검증 추가 및 태그 내에서 검증하도록 로직 수정 * refactor: 게임 서브 태그 컴포넌트에 key 값 추가 * refactor: 태그 검증 함수 일괄 처리 및 태그 등록 함수 수정 * feat: 밸런스 게임 모달 텍스트 상수 적용 및 신고 모달 제시 연결
1 parent 4329a04 commit b2be48d

File tree

16 files changed

+217
-48
lines changed

16 files changed

+217
-48
lines changed

src/App.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ const App: React.FC = () => {
148148
/>
149149
<Route
150150
path={PATH.BALANCEGAME.EDIT()}
151-
element={<BalanceGameEditPage />}
151+
element={!isMobile && <BalanceGameEditPage />}
152152
/>
153153
<Route
154154
path={PATH.CHANGE.PROFILE}

src/components/atoms/ToastModal/ToastModal.style.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ export const getToastModalColor = (
2020
return style[bgColor];
2121
};
2222

23-
export const toastModalStyling = css(typo.Main.SemiBold, {
23+
export const toastModalStyling = css({
24+
...typo.Main.SemiBold,
2425
display: 'flex',
2526
justifyContent: 'center',
2627
alignItems: 'center',
@@ -29,6 +30,11 @@ export const toastModalStyling = css(typo.Main.SemiBold, {
2930
padding: '20px 30px',
3031
borderRadius: '35px',
3132
boxShadow: '1px 1px 10px rgba(0, 0, 0, 0.07)',
33+
34+
'@media (max-width: 430px)': {
35+
...typo.Mobile.Text.SemiBold_12,
36+
padding: '14px 24px',
37+
},
3238
});
3339

3440
export const toastContainer = css({

src/components/mobile/molecules/GameTagModal/GameTagModal.tsx

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { TAG_OPTIONS } from '@/constants/game';
55
import Modal from '@/components/mobile/atoms/Modal/Modal';
66
import Button from '@/components/mobile/atoms/Button/Button';
77
import Divider from '@/components/atoms/Divider/Divider';
8+
import { validateGameTag } from '@/hooks/game/validateBalanceGameForm';
89
import * as S from './GameTagModal.style';
910

1011
interface GameTagModalProps {
@@ -31,10 +32,13 @@ const GameTagModal = ({
3132
};
3233

3334
const handleTagSubmit = () => {
34-
if (currentMainTag) {
35-
submitGame();
36-
onClose?.();
37-
}
35+
if (!currentMainTag) return;
36+
37+
const { isValid } = validateGameTag(form);
38+
if (!isValid) return;
39+
40+
submitGame();
41+
onClose?.();
3842
};
3943

4044
return (
@@ -70,7 +74,6 @@ const GameTagModal = ({
7074
name="subTag"
7175
css={S.inputStyling}
7276
placeholder="ex. 너무어려운밸런스게임, 선택장애, 이상형"
73-
maxLength={10}
7477
value={form.subTag}
7578
onChange={setSubTagValue}
7679
/>

src/components/mobile/organisms/BalanceGameCreateSection/BalanceGameCreateSection.tsx

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import React, { useState } from 'react';
22
import { BUTTON_TEXT, MAX_STAGE } from '@/constants/game';
3+
import { GameSet } from '@/types/game';
34
import ToastModal from '@/components/atoms/ToastModal/ToastModal';
45
import Button from '@/components/mobile/atoms/Button/Button';
56
import GameStageLabel from '@/components/mobile/atoms/GameStageLabel/GameStageLabel';
@@ -12,7 +13,15 @@ import TempGameModal from '@/components/mobile/molecules/TempGameModal/TempGameM
1213
import { usePostBalanceGameForm } from '@/hooks/game/usePostBalanceGameForm';
1314
import * as S from './BalanceGameCreateSection.style';
1415

15-
const BalanceGameCreateSection = () => {
16+
interface BalanceGameCreateSectionProps {
17+
existingGame?: GameSet;
18+
gameSetId?: number;
19+
}
20+
21+
const BalanceGameCreateSection = ({
22+
existingGame,
23+
gameSetId,
24+
}: BalanceGameCreateSectionProps) => {
1625
const [gameStage, setGameStage] = useState<number>(0);
1726

1827
const [tagModalOpen, setTagModalOpen] = useState<boolean>(false);
@@ -34,7 +43,13 @@ const BalanceGameCreateSection = () => {
3443
handleBalanceGame,
3544
handleTempBalanceGame,
3645
handleDraftButton,
37-
} = usePostBalanceGameForm(gameStage, setGameStage, setTagModalOpen);
46+
} = usePostBalanceGameForm(
47+
gameStage,
48+
setGameStage,
49+
setTagModalOpen,
50+
existingGame,
51+
gameSetId,
52+
);
3853

3954
return (
4055
<form css={S.balanceGameStyling}>
@@ -79,7 +94,8 @@ const BalanceGameCreateSection = () => {
7994
}}
8095
onConfirm={() => {
8196
handleDeleteImg(
82-
form.games[gameStage].gameOptions[selectedOptionId].fileId,
97+
form.games[gameStage].gameOptions[selectedOptionId].fileId ??
98+
null,
8399
selectedOptionId,
84100
);
85101
setImgDeleteModalOpen(false);

src/components/mobile/organisms/BalanceGameEndingSection/BalanceGameEndingSection.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import {
77
} from '@/assets';
88
import { PATH } from '@/constants/path';
99
import { useNavigate } from 'react-router-dom';
10+
import { useNewSelector } from '@/store';
11+
import { selectAccessToken } from '@/store/auth';
1012
import { useGameEndBookmark } from '@/hooks/game/useBalanceGameBookmark';
1113
import useToastModal from '@/hooks/modal/useToastModal';
1214
import ToastModal from '@/components/atoms/ToastModal/ToastModal';
@@ -29,7 +31,7 @@ const BalanceGameEndingSection = ({
2931
isMyEndBookmark,
3032
}: BalanceGameEndingSectionProps) => {
3133
const navigate = useNavigate();
32-
const isGuest = !localStorage.getItem('accessToken');
34+
const isGuest = !useNewSelector(selectAccessToken);
3335

3436
const [shareModalOpen, setShareModalOpen] = useState<boolean>(false);
3537
const { isVisible, modalText, showToastModal } = useToastModal();

src/components/mobile/organisms/BalanceGameSection/BalanceGameSection.style.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ export const subTagWrapper = css({
8080
display: 'flex',
8181
width: '100%',
8282
marginBottom: '50px',
83+
gap: '8px',
8384
});
8485

8586
export const iconButtonWrapper = css({

src/components/mobile/organisms/BalanceGameSection/BalanceGameSection.tsx

Lines changed: 78 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
import React, { useState, useEffect, useRef } from 'react';
22
import { MobileBookmarkDF, MobileBookmarkPR, MobileShare } from '@/assets';
33
import { useNavigate } from 'react-router-dom';
4+
import { useNewSelector } from '@/store';
5+
import { selectAccessToken } from '@/store/auth';
46
import { GameDetail, GameSet } from '@/types/game';
7+
import { createArrayFromCommaString } from '@/utils/array';
58
import { PATH } from '@/constants/path';
9+
import { ERROR, PROMPT } from '@/constants/message';
610
import MenuTap, { MenuItem } from '@/components/atoms/MenuTap/MenuTap';
711
import useToastModal from '@/hooks/modal/useToastModal';
812
import { VoteRecord } from '@/types/vote';
@@ -15,7 +19,10 @@ import ToastModal from '@/components/atoms/ToastModal/ToastModal';
1519
import BalanceGameBox from '@/components/mobile/molecules/BalanceGameBox/BalanceGameBox';
1620
import { useGuestGameVote } from '@/hooks/game/useBalanceGameVote';
1721
import { useGameBookmark } from '@/hooks/game/useBalanceGameBookmark';
18-
import ShareModal from '../../molecules/ShareModal/ShareModal';
22+
import { useDeleteGameSetMutation } from '@/hooks/api/game/useDeleteGameSetMutation';
23+
import ShareModal from '@/components/mobile/molecules/ShareModal/ShareModal';
24+
import TextModal from '@/components/mobile/molecules/TextModal/TextModal';
25+
import ReportModal from '@/components/mobile/molecules/ReportModal/ReportModal';
1926
import * as S from './BalanceGameSection.style';
2027

2128
export interface BalanceGameSectionProps {
@@ -51,11 +58,13 @@ const BalanceGameSection = ({
5158

5259
const gameStages: GameDetail[] =
5360
game?.gameDetailResponses ?? gameDefaultDetail;
54-
const isGuest = !localStorage.getItem('accessToken');
61+
const isGuest = !useNewSelector(selectAccessToken);
5562

5663
const [guestVotedList, setGuestVotedList] = useState<VoteRecord[]>([]);
5764

5865
const currentGame: GameDetail = gameStages[currentStage];
66+
const subTagList = createArrayFromCommaString(game?.subTag ?? '');
67+
5968
const { handleGuestGameVote } = useGuestGameVote(
6069
guestVotedList,
6170
setGuestVotedList,
@@ -64,8 +73,16 @@ const BalanceGameSection = ({
6473
game,
6574
);
6675

67-
const [shareModalOpen, setShareModalOpen] = useState<boolean>(false);
6876
const { isVisible, modalText, showToastModal } = useToastModal();
77+
const { mutate: deleteBalanceGame } = useDeleteGameSetMutation();
78+
79+
const [activeModal, setActiveModal] = useState<
80+
'reportGame' | 'reportText' | 'deleteText' | 'share' | 'none'
81+
>('none');
82+
83+
const onCloseModal = () => {
84+
setActiveModal('none');
85+
};
6986

7087
useEffect(() => {
7188
if (game && initialRender.current) {
@@ -89,6 +106,20 @@ const BalanceGameSection = ({
89106
changeStage(1);
90107
};
91108

109+
const handleGameDeleteButton = () => {
110+
deleteBalanceGame(
111+
{ gameSetId },
112+
{
113+
onSuccess: () => {
114+
navigate('/');
115+
},
116+
onError: () => {
117+
showToastModal(ERROR.DELETEGAME.FAIL);
118+
},
119+
},
120+
);
121+
};
122+
92123
const { handleBookmarkClick } = useGameBookmark(
93124
isGuest,
94125
isMyGame,
@@ -99,8 +130,28 @@ const BalanceGameSection = ({
99130
game,
100131
);
101132

102-
const myGameItem: MenuItem[] = [{ label: '수정' }, { label: '삭제' }];
103-
const otherGameItem: MenuItem[] = [{ label: '신고' }];
133+
const myGameItem: MenuItem[] = [
134+
{
135+
label: '수정',
136+
onClick: () => {
137+
navigate(`/${PATH.CREATE.GAME}`, { state: { game, gameSetId } });
138+
},
139+
},
140+
{
141+
label: '삭제',
142+
onClick: () => {
143+
setActiveModal('deleteText');
144+
},
145+
},
146+
];
147+
const otherGameItem: MenuItem[] = [
148+
{
149+
label: '신고',
150+
onClick: () => {
151+
setActiveModal('reportText');
152+
},
153+
},
154+
];
104155

105156
return (
106157
<div css={S.balanceGameStyling}>
@@ -111,17 +162,34 @@ const BalanceGameSection = ({
111162
)}
112163
<div css={S.centerStyling}>
113164
<ShareModal
114-
isOpen={shareModalOpen}
165+
isOpen={activeModal === 'share'}
166+
onConfirm={() => {}}
167+
onClose={onCloseModal}
168+
/>
169+
<TextModal
170+
text={PROMPT.GAME.DELETE}
171+
isOpen={activeModal === 'deleteText'}
172+
onConfirm={handleGameDeleteButton}
173+
onClose={onCloseModal}
174+
/>
175+
<TextModal
176+
text={PROMPT.GAME.REPORT}
177+
isOpen={activeModal === 'reportText'}
178+
onConfirm={() => setActiveModal('reportGame')}
179+
onClose={onCloseModal}
180+
/>
181+
<ReportModal
182+
isOpen={activeModal === 'reportGame'}
115183
onConfirm={() => {}}
116-
onClose={() => setShareModalOpen(false)}
184+
onClose={onCloseModal}
117185
/>
118186
</div>
119187
<div css={S.balancGameTopWrapper}>
120188
<GameTag tag={game?.mainTag ?? ''} />
121189
<div css={S.iconButtonWrapper}>
122190
<IconButton
123191
icon={<MobileShare />}
124-
onClick={() => setShareModalOpen(true)}
192+
onClick={() => setActiveModal('share')}
125193
/>
126194
<IconButton
127195
icon={
@@ -164,7 +232,8 @@ const BalanceGameSection = ({
164232
/>
165233
</div>
166234
<div css={S.subTagWrapper}>
167-
{game.subTag && <GameTagChip tag={game.subTag} />}
235+
{game.subTag &&
236+
subTagList.map((tag) => <GameTagChip key={tag} tag={tag} />)}
168237
</div>
169238
</div>
170239
)}

src/components/molecules/BalanceGameEndingBox/BalanceGameEndingBox.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import ShareModal from '@/components/molecules/ShareModal/ShareModal';
99
import LoginModal from '@/components/molecules/LoginModal/LoginModal';
1010
import useToastModal from '@/hooks/modal/useToastModal';
1111
import { useGameEndBookmark } from '@/hooks/game/useBalanceGameBookmark';
12+
import { useNewSelector } from '@/store';
13+
import { selectAccessToken } from '@/store/auth';
1214
import * as S from './BalanceGameEndingBox.style';
1315

1416
export interface BalanceGameEndingBoxProps {
@@ -25,7 +27,7 @@ const BalanceGameEndingBox = ({
2527
isMyEndBookmark,
2628
}: BalanceGameEndingBoxProps) => {
2729
const currentURL: string = window.location.href;
28-
const isGuest = !localStorage.getItem('accessToken');
30+
const isGuest = !useNewSelector(selectAccessToken);
2931

3032
const [loginModalOpen, setLoginModalOpen] = useState<boolean>(false);
3133
const [shareModalOpen, setShareModalOpen] = useState<boolean>(false);

src/hooks/api/game/useCreateGameMutation.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ export const useCreateGameMutation = (
2121
queryClient.invalidateQueries({
2222
queryKey: ['games'],
2323
});
24-
showToastModal(SUCCESS.CREATEGAME.CREATE, () => {
24+
showToastModal(SUCCESS.GAME.CREATE, () => {
2525
navigate(`/${PATH.BALANCEGAME.VIEW(gameId)}`);
2626
});
2727
},

0 commit comments

Comments
 (0)