-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #144 from The-Fellowship-of-the-matzip/develop
1.2.0 버전 배포 (랜덤 룰렛, UI 수정)
- Loading branch information
Showing
10 changed files
with
362 additions
and
5 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
98 changes: 98 additions & 0 deletions
98
src/components/pages/CategoryPage/RandomRoulette/RandomRoulette.style.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
import styled, { css } from "styled-components"; | ||
|
||
export const Container = styled.section` | ||
width: 100%; | ||
display: flex; | ||
flex-direction: column; | ||
align-items: center; | ||
gap: 1rem; | ||
`; | ||
|
||
export const RecommendBlock = styled.div` | ||
display: flex; | ||
justify-content: center; | ||
align-items: center; | ||
gap: 0.5rem; | ||
`; | ||
|
||
export const Label = styled.p` | ||
text-align: center; | ||
`; | ||
|
||
export const Outer = styled.div` | ||
height: 4rem; | ||
width: 14rem; | ||
overflow: hidden; | ||
padding: 1rem 0.5rem; | ||
`; | ||
|
||
export const Inner = styled.div<{ runAnimation: boolean }>` | ||
font-size: 1.2rem; | ||
display: flex; | ||
flex-direction: column; | ||
gap: 1rem; | ||
${({ runAnimation }) => | ||
runAnimation && | ||
css` | ||
animation: slider ease-in-out 5s; | ||
transform: translateY(calc(-100% + 2rem)); | ||
`} | ||
@keyframes slider { | ||
0% { | ||
transform: translateY(0); | ||
} | ||
10% { | ||
transform: translateY(1.5rem); | ||
} | ||
85% { | ||
transform: translateY(calc(-100% + 0.5rem)); | ||
} | ||
100% { | ||
transform: translateY(calc(-100% + 2rem)); | ||
} | ||
} | ||
`; | ||
|
||
export const RouletteSlot = styled.div` | ||
text-align: center; | ||
height: 2rem; | ||
line-height: 2rem; | ||
width: 13rem; | ||
overflow: hidden; | ||
white-space: nowrap; | ||
text-overflow: ellipsis; | ||
`; | ||
|
||
export const ResultWrapper = styled.div` | ||
width: 100%; | ||
border-bottom: 0.065rem solid ${({ theme }) => theme.secondary}; | ||
transform-origin: top; | ||
animation: slide-from-top 0.2s ease; | ||
@keyframes slide-from-top { | ||
0% { | ||
transform: scaleY(0); | ||
} | ||
100% { | ||
transform: scaleY(1); | ||
} | ||
} | ||
`; | ||
|
||
export const ButtonContainer = styled.div` | ||
display: flex; | ||
justify-content: center; | ||
gap: 0.5rem; | ||
`; | ||
|
||
export const CustomButton = styled.button` | ||
background-color: ${({ theme }) => theme.primary}; | ||
color: ${({ theme }) => theme.white}; | ||
border: none; | ||
border-radius: 0.25rem; | ||
padding: 0.5rem 0.8rem; | ||
box-shadow: 1px 1px 1px #8d8d8d; | ||
font-weight: 600; | ||
`; |
97 changes: 97 additions & 0 deletions
97
src/components/pages/CategoryPage/RandomRoulette/RandomRoulette.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
import { useEffect } from "react"; | ||
import { useQuery } from "react-query"; | ||
|
||
import { NETWORK, SIZE } from "constants/api"; | ||
|
||
import useRandomPick from "hooks/useRandomPick"; | ||
|
||
import fetchRandomStoreList from "api/fetchRandomStoreList"; | ||
|
||
import ErrorImage from "components/common/ErrorImage/ErrorImage"; | ||
import Spinner from "components/common/Spinner/Spinner"; | ||
import StoreListItem from "components/common/StoreListItem/StoreListItem"; | ||
|
||
import * as S from "components/pages/CategoryPage/RandomRoulette/RandomRoulette.style"; | ||
|
||
type Props = { | ||
campusId: 1 | 2; | ||
}; | ||
|
||
function RandomRoulette({ campusId }: Props) { | ||
const { | ||
data: stores, | ||
isLoading, | ||
isError, | ||
error, | ||
refetch, | ||
} = useQuery( | ||
"randomStoreRoulette", | ||
() => fetchRandomStoreList(campusId, SIZE.RANDOM_ITEM), | ||
{ | ||
retry: NETWORK.RETRY_COUNT, | ||
refetchOnWindowFocus: false, | ||
} | ||
); | ||
|
||
const { | ||
state: { rouletteBoard, isResultOpen, result, triggerAnimation }, | ||
handleRunClick, | ||
openResult, | ||
reset, | ||
} = useRandomPick(stores || []); | ||
|
||
const resetHard = () => { | ||
refetch(); | ||
reset(); | ||
}; | ||
|
||
useEffect(() => { | ||
resetHard(); | ||
}, [campusId]); | ||
|
||
return ( | ||
<S.Container> | ||
{isLoading && <Spinner />} | ||
{isError && error instanceof Error && ( | ||
<ErrorImage errorMessage={error.message} /> | ||
)} | ||
<S.RecommendBlock> | ||
<S.Label>오늘은 </S.Label> | ||
<S.Outer> | ||
<S.Inner runAnimation={triggerAnimation} onAnimationEnd={openResult}> | ||
{rouletteBoard.map((store, index) => ( | ||
<S.RouletteSlot key={store + index}>{store}</S.RouletteSlot> | ||
))} | ||
</S.Inner> | ||
</S.Outer> | ||
<S.Label>어때요?</S.Label> | ||
</S.RecommendBlock> | ||
{!isResultOpen ? ( | ||
<S.CustomButton | ||
onClick={handleRunClick} | ||
disabled={result !== undefined} | ||
> | ||
룰렛 Go! | ||
</S.CustomButton> | ||
) : ( | ||
<S.ButtonContainer> | ||
<S.CustomButton onClick={reset}>다시 돌리기</S.CustomButton> | ||
<S.CustomButton onClick={resetHard}>식당 리셋하기</S.CustomButton> | ||
</S.ButtonContainer> | ||
)} | ||
{isResultOpen && result !== undefined && ( | ||
<S.ResultWrapper> | ||
<StoreListItem | ||
id={result.id} | ||
thumbnailUrl={result.imageUrl} | ||
name={result.name} | ||
distance={result.distance} | ||
rating={result.rating} | ||
/> | ||
</S.ResultWrapper> | ||
)} | ||
</S.Container> | ||
); | ||
} | ||
|
||
export default RandomRoulette; |
69 changes: 69 additions & 0 deletions
69
src/components/pages/CategoryPage/RandomRoulette/randomRouletteStateReducer.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
import type { Store } from "mock/data"; | ||
|
||
export const ACTION_TYPES = { | ||
SET_BOARD: "SET_BOARD", | ||
SPIN: "SPIN", | ||
SHOW_RESULT: "SHOW_RESULT", | ||
RESET: "RESET", | ||
} as const; | ||
|
||
type State = { | ||
triggerAnimation: boolean; | ||
rouletteBoard: string[]; | ||
pickedIndex: number | null; | ||
result: Store | undefined; | ||
isResultOpen: boolean; | ||
}; | ||
|
||
type Action = | ||
| { | ||
type: typeof ACTION_TYPES.SET_BOARD; | ||
payload: Pick<State, "rouletteBoard">; | ||
} | ||
| { | ||
type: typeof ACTION_TYPES.SPIN; | ||
payload: Pick<State, "pickedIndex" | "result" | "rouletteBoard">; | ||
} | ||
| { | ||
type: typeof ACTION_TYPES.SHOW_RESULT; | ||
} | ||
| { | ||
type: typeof ACTION_TYPES.RESET; | ||
payload: Pick<State, "rouletteBoard">; | ||
}; | ||
|
||
export const initialState: State = { | ||
triggerAnimation: false, | ||
rouletteBoard: [], | ||
pickedIndex: null, | ||
result: undefined, | ||
isResultOpen: false, | ||
}; | ||
|
||
export const randomRouletteStateReducer = (state: State, action: Action) => { | ||
switch (action.type) { | ||
case ACTION_TYPES.SET_BOARD: { | ||
return { ...state, rouletteBoard: action.payload.rouletteBoard }; | ||
} | ||
case ACTION_TYPES.SPIN: { | ||
const { pickedIndex, result, rouletteBoard } = action.payload; | ||
|
||
return { | ||
...state, | ||
pickedIndex, | ||
result, | ||
rouletteBoard, | ||
triggerAnimation: true, | ||
}; | ||
} | ||
case ACTION_TYPES.SHOW_RESULT: { | ||
return { ...state, isResultOpen: true }; | ||
} | ||
case ACTION_TYPES.RESET: { | ||
return { ...initialState, rouletteBoard: action.payload.rouletteBoard }; | ||
} | ||
|
||
default: | ||
return initialState; | ||
} | ||
}; |
Oops, something went wrong.