diff --git a/package-lock.json b/package-lock.json index 6d6e2a8f7..9f18c6c9f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15686,9 +15686,9 @@ "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" }, "react-modal": { - "version": "3.14.3", - "resolved": "https://registry.npmjs.org/react-modal/-/react-modal-3.14.3.tgz", - "integrity": "sha512-+C2KODVKyu20zHXPJxfOOcf571L1u/EpFlH+oS/3YDn8rgVE51QZuxuuIwabJ8ZFnOEHaD+r6XNjqwtxZnXO0g==", + "version": "3.14.4", + "resolved": "https://registry.npmjs.org/react-modal/-/react-modal-3.14.4.tgz", + "integrity": "sha512-8surmulejafYCH9wfUmFyj4UfbSJwjcgbS9gf3oOItu4Hwd6ivJyVBETI0yHRhpJKCLZMUtnhzk76wXTsNL6Qg==", "requires": { "exenv": "^1.2.0", "prop-types": "^15.7.2", diff --git a/package.json b/package.json index 9b2085a00..37bcba9e2 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "react-cookie": "^4.1.1", "react-dom": "^17.0.2", "react-idle-timer": "^4.6.4", - "react-modal": "^3.14.3", + "react-modal": "^3.14.4", "react-redux": "^7.2.5", "react-router-dom": "^5.3.0", "react-swipeable": "^6.2.0", diff --git a/public/assets/mission/mission_1.png b/public/assets/mission/mission_1.png new file mode 100644 index 000000000..653478880 Binary files /dev/null and b/public/assets/mission/mission_1.png differ diff --git a/public/assets/mission/mission_list.png b/public/assets/mission/mission_list.png new file mode 100644 index 000000000..12646363b Binary files /dev/null and b/public/assets/mission/mission_list.png differ diff --git a/src/components/Button/Button.tsx b/src/components/Button/Button.tsx index 4b55767b3..44160de14 100644 --- a/src/components/Button/Button.tsx +++ b/src/components/Button/Button.tsx @@ -190,7 +190,7 @@ const ButtonComponent: React.FC = (props) => { export const Button = React.memo(ButtonComponent); -export const DisabledButton: React.FC = (props) => { +const DisabledButtonComponent: React.FC = (props) => { return ( = (props) => { ); }; + +export const DisabledButton = React.memo(DisabledButtonComponent); diff --git a/src/index.css b/src/index.css index a5ffe6a22..1f1f6fb3e 100644 --- a/src/index.css +++ b/src/index.css @@ -33,7 +33,7 @@ body { #root { height: 100%; } -*:not(input) { +*:not(input, textarea) { user-select: none; } *:focus { diff --git a/src/pages/Game2048/Game/Game.tsx b/src/pages/Game2048/Game/Game.tsx index 1eacb599a..223ba2654 100644 --- a/src/pages/Game2048/Game/Game.tsx +++ b/src/pages/Game2048/Game/Game.tsx @@ -3,12 +3,11 @@ import { useCurrentScreen } from '@karrotframe/navigator'; import { Button } from 'components/Button'; import { rem } from 'polished'; import React, { useCallback, useEffect, useState } from 'react'; -import ReactModal from 'react-modal'; import { useMinigameApi } from 'services/api/minigameApi'; import { useMyGame2048Data } from '../hooks'; import { Board } from './Game/Board'; import { useGame } from './hooks'; -import { GameOver } from './Modal'; +import { GameOverModal } from './Modal'; import { MemoizedCurrentScore as CurrentScore, MemoizedMyBestScore as MyBestScore, @@ -18,6 +17,7 @@ import refreshGameUrl from 'assets/svg/game2048/refresh_game.svg'; import { useAnalytics } from 'services/analytics'; import { useMini, useUserData } from 'hooks'; import { useDebouncedCallback } from 'use-debounce'; +import ReactModal from 'react-modal'; export const Game: React.FC = () => { const analytics = useAnalytics(); @@ -364,8 +364,7 @@ export const Game: React.FC = () => { { }, }} > - + ); diff --git a/src/pages/Game2048/Game/Game/Board/Board.tsx b/src/pages/Game2048/Game/Game/Board/Board.tsx index a0938b7f8..2eb465356 100644 --- a/src/pages/Game2048/Game/Game/Board/Board.tsx +++ b/src/pages/Game2048/Game/Game/Board/Board.tsx @@ -1,10 +1,10 @@ -import React, { useEffect, useLayoutEffect, useRef, useState } from 'react'; +import React, { useLayoutEffect, useRef, useState } from 'react'; import styled from '@emotion/styled'; import { MemoizedTile as Tile, TileProps } from '../Tile'; -import { animationDuration, boardMargin, boardPadding } from '../styles'; +import { boardMargin, boardPadding } from '../styles'; import { useSwipeable } from 'react-swipeable'; import { Guide } from './Guide'; -import { useThrottledCallback } from 'use-debounce/lib'; + import { MemoizedGrid as Grid } from '.'; type Props = { @@ -23,7 +23,7 @@ export const Board: React.FC = (props) => { handlers.ref(el); tileContainerRef.current = el; }; - // ================================================================= + // mobile(touch) friendly const handlers = useSwipeable({ onSwipeStart: (eventData) => { @@ -46,36 +46,6 @@ export const Board: React.FC = (props) => { preventDefaultTouchmoveEvent: true, trackTouch: true, }); - // desktop(keyboard) friendly - const handleKeyDown = useThrottledCallback( - (e: KeyboardEvent) => { - // disables page scrolling with keyboard arrows - e.preventDefault(); - switch (e.code) { - case 'ArrowRight': - props.moveRight(); - break; - case 'ArrowLeft': - props.moveLeft(); - break; - case 'ArrowUp': - props.moveUp(); - break; - case 'ArrowDown': - props.moveDown(); - break; - } - }, - animationDuration - // { leading: true, trailing: false } - ); - useEffect(() => { - window.addEventListener('keydown', handleKeyDown); - return () => { - window.removeEventListener('keydown', handleKeyDown); - }; - }, [handleKeyDown]); - // ================================================================= // change board & tile size responsively to window size useLayoutEffect(() => { diff --git a/src/pages/Game2048/Game/Modal/PostComment.tsx b/src/pages/Game2048/Game/Modal/CommentModal.tsx similarity index 77% rename from src/pages/Game2048/Game/Modal/PostComment.tsx rename to src/pages/Game2048/Game/Modal/CommentModal.tsx index fc9354a2e..1e5b30faf 100644 --- a/src/pages/Game2048/Game/Modal/PostComment.tsx +++ b/src/pages/Game2048/Game/Modal/CommentModal.tsx @@ -16,7 +16,7 @@ type Props = { rank: number; }; -export const PostComment: React.FC = (props) => { +export const CommentModal: React.FC = (props) => { const analytics = useAnalytics(); const { isTop } = useCurrentScreen(); const { replace } = useNavigator(); @@ -24,51 +24,28 @@ export const PostComment: React.FC = (props) => { const { townName2: districtName } = useUserData(); const { isInWebEnvironment } = useMini(); const { comment: prevComment, updateMyComment } = useMyGame2048Data(); - const [currentComment, setCurrentComment] = useState({ - comment: prevComment, - length: prevComment.length, - }); + const [comment, setComment] = useState(''); // Page navigation const goToLeaderboardPage = () => { replace(`/game-2048/leaderboard`); }; - - const handleDeleteCharacter = (e: any) => { - console.log('onKeyDown', e, e.keyCode, e.key); - if (e.keyCode === 8 || e.key === 'Backspace') { - if (e.target.defaultValue === '') { - setCurrentComment({ - comment: '', - length: 0, - }); - } else { - setCurrentComment((prevState) => ({ - comment: prevState.comment.slice(0, -1), - length: prevState.length - 1, - })); - } - } - }; - const handleCommentInput = (e: React.ChangeEvent) => { - setCurrentComment({ - comment: e.target.value.slice(0, 19), - length: e.target.value.length, - }); + const { value, maxLength } = e.target; + setComment(value.slice(0, maxLength)); }; - const patchComment = async () => { + const patchComment = async ({ comment }: { comment: string }) => { if (isInWebEnvironment) { - updateMyComment(currentComment.comment); + updateMyComment(comment); goToLeaderboardPage(); return; } try { - updateMyComment(currentComment.comment); + updateMyComment(comment); const { data } = await minigameApi.gamePlayApi.addCommentUsingPATCH( 'GAME_2048', { - comment: currentComment.comment, + comment: comment, } ); if (data.status === 200) { @@ -118,19 +95,22 @@ export const PostComment: React.FC = (props) => { autoFocus type="text" maxLength={20} - placeholder={`예) 오예~${districtName}짱! :)`} - onChange={handleCommentInput} - onKeyDown={handleDeleteCharacter} - value={currentComment.comment} + placeholder={ + prevComment === '' || prevComment === null + ? `예) 오예~${districtName}짱! :)` + : `${prevComment}` + } + onChange={(e) => handleCommentInput(e)} + value={comment} /> - {currentComment.length}/20 + {comment.length}/20 - {currentComment.length > 0 ? ( + {comment.length > 0 ? ( diff --git a/src/pages/Game2048/Game/Modal/GameOver.tsx b/src/pages/Game2048/Game/Modal/GameOverModal.tsx similarity index 93% rename from src/pages/Game2048/Game/Modal/GameOver.tsx rename to src/pages/Game2048/Game/Modal/GameOverModal.tsx index 9229f94e0..8f1b03327 100644 --- a/src/pages/Game2048/Game/Modal/GameOver.tsx +++ b/src/pages/Game2048/Game/Modal/GameOverModal.tsx @@ -1,8 +1,7 @@ -import { useCurrentScreen, useNavigator } from '@karrotframe/navigator'; import styled from '@emotion/styled'; import React, { useState, useEffect, useCallback } from 'react'; -import ReactModal from 'react-modal'; -import { PostComment } from './PostComment'; +import { useCurrentScreen, useNavigator } from '@karrotframe/navigator'; +import { CommentModal } from './CommentModal'; import gameOverSvgUrl from 'assets/svg/game2048/gameover.svg'; import { Button } from 'components/Button'; import { useMinigameApi } from 'services/api/minigameApi'; @@ -17,11 +16,14 @@ import { fireRandomDirectionConfetti, } from 'utils/functions/confetti'; import { useThrottledCallback } from 'use-debounce/lib'; +import ReactModal from 'react-modal'; + type Props = { myPreviousRank: number; currentScore: number; }; -export const GameOver: React.FC = (props) => { + +export const GameOverModal: React.FC = (props) => { const { isTop } = useCurrentScreen(); const { replace } = useNavigator(); const analytics = useAnalytics(); @@ -29,7 +31,8 @@ export const GameOver: React.FC = (props) => { const { isInWebEnvironment, shareApp } = useMini(); const { nickname } = useUserData(); const { gameType } = useMyGame2048Data(); - const [shouldModalOpen, setShouldModalOpen] = useState(false); + // const [shouldModalOpen, setShouldModalOpen] = useState(false); + const [isCommentModalOpen, setIsCommentModalOpen] = useState(false); const [sessionRank, setSessionRank] = useState<{ rank: number | undefined; score: number | undefined; @@ -91,18 +94,16 @@ export const GameOver: React.FC = (props) => { ); useEffect(() => { - if (isTop) { - getSessionRank({ gameType: gameType, score: props.currentScore }); - getMyCurrentRank({ - gameType: gameType, - previousRank: props.myPreviousRank, - }); - } + getSessionRank({ gameType: gameType, score: props.currentScore }); + getMyCurrentRank({ + gameType: gameType, + previousRank: props.myPreviousRank, + }); }, [ gameType, getMyCurrentRank, getSessionRank, - isTop, + props.currentScore, props.myPreviousRank, ]); @@ -123,8 +124,10 @@ export const GameOver: React.FC = (props) => { // button to view leaderbaord (open commment modal if condition is met) const handleViewLeaderboard = () => { + setIsCommentModalOpen(true); if (isInWebEnvironment) { - goToLeaderboardPage(); + // goToLeaderboardPage(); + setIsCommentModalOpen(true); return; } analytics.logEvent('click_view_leaderboard_button', { @@ -132,7 +135,7 @@ export const GameOver: React.FC = (props) => { }); if (sessionRank.rank !== undefined) { sessionRank.rank > 0 && sessionRank.rank <= 10 - ? setShouldModalOpen(true) + ? setIsCommentModalOpen(true) : goToLeaderboardPage(); } }; @@ -166,6 +169,7 @@ export const GameOver: React.FC = (props) => { colors: [`#0E74FF`, `#82B6FF`, `#E3EFFF`], }); }, 3000); + return ( <>
= (props) => { = (props) => { }, }} > - diff --git a/src/pages/Game2048/Game/Modal/index.ts b/src/pages/Game2048/Game/Modal/index.ts index b1e9296a3..e38afe9ea 100644 --- a/src/pages/Game2048/Game/Modal/index.ts +++ b/src/pages/Game2048/Game/Modal/index.ts @@ -1,2 +1,2 @@ -export * from './PostComment'; -export * from './GameOver'; +export * from './CommentModal'; +export * from './GameOverModal'; diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx index b94d4411c..6aec5c4ec 100644 --- a/src/pages/Home.tsx +++ b/src/pages/Home.tsx @@ -452,11 +452,9 @@ export const Home: React.FC = () => { }, [getLastWeekTopUsers]); useEffect(() => { - if (isTop) { - retreieveTop2048PuzzleUsers(); - retrieveTopKarrotClickerUsers(); - } - }, [isTop, retreieveTop2048PuzzleUsers, retrieveTopKarrotClickerUsers]); + retreieveTop2048PuzzleUsers(); + retrieveTopKarrotClickerUsers(); + }, [retreieveTop2048PuzzleUsers, retrieveTopKarrotClickerUsers]); return (
diff --git a/src/pages/KarrotClicker/Game/Game.tsx b/src/pages/KarrotClicker/Game/Game.tsx index 1f46cc398..7a82396cc 100644 --- a/src/pages/KarrotClicker/Game/Game.tsx +++ b/src/pages/KarrotClicker/Game/Game.tsx @@ -18,7 +18,7 @@ import { useMinigameApi } from 'services/api/minigameApi'; Modal.setAppElement(document.createElement('div')); -export const Game = () => { +export const Game: React.FC = () => { const { isTop } = useCurrentScreen(); const analytics = useAnalytics(); const { userId, setUserInfo } = useUserData(); diff --git a/src/pages/KarrotClicker/Game/Modal/CommentModal.tsx b/src/pages/KarrotClicker/Game/Modal/CommentModal.tsx index b8a804ecc..0b4e7b888 100644 --- a/src/pages/KarrotClicker/Game/Modal/CommentModal.tsx +++ b/src/pages/KarrotClicker/Game/Modal/CommentModal.tsx @@ -67,7 +67,7 @@ export const CommentModal: React.FC = (props) => { game_type: 'karrot_clicker', }); } - }, [analytics, isTop, prevComment, rank]); + }, [analytics, isTop]); return ( <> diff --git a/src/pages/KarrotClicker/Game/Modal/GameOver.tsx b/src/pages/KarrotClicker/Game/Modal/GameOver.tsx index 8c7c75433..7c35c3190 100644 --- a/src/pages/KarrotClicker/Game/Modal/GameOver.tsx +++ b/src/pages/KarrotClicker/Game/Modal/GameOver.tsx @@ -14,7 +14,6 @@ import { useMinigameApi } from 'services/api/minigameApi'; import { useMini } from 'hooks'; import { useGame } from '../hooks'; import { rem } from 'polished'; - ReactModal.setAppElement(document.createElement('div')); type Props = { @@ -39,6 +38,7 @@ export const GameOver: React.FC = (props) => { if (isInWebEnvironment) { props.setIsGameOver(false); goToLeaderboardPage(); + return; } try { diff --git a/src/services/openapi_generator/base.ts b/src/services/openapi_generator/base.ts index 9a68e8f85..f3a920129 100644 --- a/src/services/openapi_generator/base.ts +++ b/src/services/openapi_generator/base.ts @@ -5,30 +5,35 @@ * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * * The version of the OpenAPI document: 1.0.0 - * + * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). * https://openapi-generator.tech * Do not edit the class manually. */ - -import { Configuration } from "./configuration"; +import { Configuration } from './configuration'; // Some imports not used depending on template conditions // @ts-ignore -import globalAxios, { AxiosPromise, AxiosInstance, AxiosRequestConfig } from 'axios'; +import globalAxios, { + AxiosPromise, + AxiosInstance, + AxiosRequestConfig, +} from 'axios'; + export const BASE_PATH = "https://api.daangn-game.com".replace(/\/+$/, ""); + /** * * @export */ export const COLLECTION_FORMATS = { - csv: ",", - ssv: " ", - tsv: "\t", - pipes: "|", + csv: ',', + ssv: ' ', + tsv: '\t', + pipes: '|', }; /** @@ -37,8 +42,8 @@ export const COLLECTION_FORMATS = { * @interface RequestArgs */ export interface RequestArgs { - url: string; - options: AxiosRequestConfig; + url: string; + options: AxiosRequestConfig; } /** @@ -47,15 +52,19 @@ export interface RequestArgs { * @class BaseAPI */ export class BaseAPI { - protected configuration: Configuration | undefined; + protected configuration: Configuration | undefined; - constructor(configuration?: Configuration, protected basePath: string = BASE_PATH, protected axios: AxiosInstance = globalAxios) { - if (configuration) { - this.configuration = configuration; - this.basePath = configuration.basePath || this.basePath; - } + constructor( + configuration?: Configuration, + protected basePath: string = BASE_PATH, + protected axios: AxiosInstance = globalAxios + ) { + if (configuration) { + this.configuration = configuration; + this.basePath = configuration.basePath || this.basePath; } -}; + } +} /** * @@ -64,8 +73,8 @@ export class BaseAPI { * @extends {Error} */ export class RequiredError extends Error { - name: "RequiredError" = "RequiredError"; - constructor(public field: string, msg?: string) { - super(msg); - } + name: 'RequiredError' = 'RequiredError'; + constructor(public field: string, msg?: string) { + super(msg); + } }