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

#622.1 랜덤박스 구매 인터렉션 #664

Merged
merged 2 commits into from
Sep 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 4 additions & 0 deletions src/components/Event/BodyRandomBox/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@

.c2023fallevent-emoji {
position: absolute;
width: 500px;
height: 500px;
border-radius: 8px;
overflow: hidden;
font-size: 250px;
transform: rotateX(-25deg) rotateY(-25deg) translate(calc(250px - 50%), 0rem);
}
Expand Down
15 changes: 14 additions & 1 deletion src/components/Event/BodyRandomBox/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,13 @@ const BodyRandomBox = ({
}
`} 2s linear infinite;
`;
const styleItem = {
position: "absolute" as const,
top: "10%",
left: "10%",
width: "80%",
height: "80%",
};

return (
<div ref={bodyRef} css={{ height: `${boxSize * 1.3}px` }}>
Expand Down Expand Up @@ -96,7 +103,13 @@ const BodyRandomBox = ({
<TopPlane css={stylePlane} />
</div>
<div className="c2023fallevent-randombox-side c2023fallevent-randombox-side-bottom" />
<div className="c2023fallevent-emoji">🎟</div>
<div className="c2023fallevent-emoji">
{itemImageUrl ? (
<img src={itemImageUrl} alt="item" css={styleItem} />
) : (
"🎟"
)}
</div>
</div>
</div>
</div>
Expand Down
65 changes: 33 additions & 32 deletions src/components/ModalPopup/ModalEvent2023FallItem.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { useCallback, useEffect, useMemo, useState } from "react";
import { Dispatch, SetStateAction, useCallback, useMemo, useRef } from "react";

import type { EventItem } from "types/event2023fall";

import { useDelayBoolean } from "hooks/useDelay";
import {
useFetchRecoilState,
useValueRecoilState,
Expand All @@ -25,39 +26,48 @@ import { ReactComponent as CreditIcon } from "static/events/2023fallCredit.svg";
type ModalEvent2023FallItemProps = Parameters<typeof Modal>[0] & {
itemInfo: EventItem;
fetchItems?: () => void;
setRewardItem?: Dispatch<SetStateAction<Nullable<EventItem>>>;
};

const ModalEvent2023FallItem = ({
itemInfo,
fetchItems,
setRewardItem,
...modalProps
}: ModalEvent2023FallItemProps) => {
const fetchEvent2023FallInfo = useFetchRecoilState("event2023FallInfo");
const event2023FallInfo = useValueRecoilState("event2023FallInfo");

const axios = useAxios();
const setAlert = useSetRecoilState(alertAtom);
const isDisplayRandomBox = !useDelayBoolean(!modalProps.isOpen, 500);
const isRequesting = useRef<boolean>(false);

const onClickOk = useCallback(
() =>
axios({
url: `/events/2023fall/items/purchase/${itemInfo._id}`,
method: "post",
onSuccess: () => {
fetchEvent2023FallInfo();
fetchItems?.();
modalProps.onChangeIsOpen?.(false);
const onClickOk = useCallback(async () => {
if (isRequesting.current) return;
isRequesting.current = true;
await axios({
url: `/events/2023fall/items/purchase/${itemInfo._id}`,
method: "post",
onSuccess: ({ reward }) => {
fetchEvent2023FallInfo();
fetchItems?.();
modalProps.onChangeIsOpen?.(false);
if (itemInfo.itemType === 3 && reward) {
setRewardItem?.(reward);
} else {
setAlert("구매가 완료되었습니다. 구매 이력에서 확인보세요.");
},
onError: () => setAlert("구매를 실패하였습니다."),
}),
[
itemInfo._id,
fetchItems,
modalProps.onChangeIsOpen,
fetchEvent2023FallInfo,
]
);
}
},
onError: () => setAlert("구매를 실패하였습니다."),
});
isRequesting.current = false;
}, [
itemInfo._id,
fetchItems,
modalProps.onChangeIsOpen,
fetchEvent2023FallInfo,
]);

const [isDisabled, buttonText] = useMemo(
() =>
Expand All @@ -73,17 +83,6 @@ const ModalEvent2023FallItem = ({
[eventMode, event2023FallInfo, itemInfo]
);

const [isDisplayRandomBox, setIsDisplayRandomBox] = useState<boolean>(false);
useEffect(() => {
if (modalProps.isOpen) {
const timeout = setTimeout(() => {
setIsDisplayRandomBox(true);
}, 500);
return () => clearTimeout(timeout);
}
setIsDisplayRandomBox(false);
}, [modalProps.isOpen]);

const styleTitle = {
...theme.font18,
display: "flex",
Expand All @@ -105,7 +104,9 @@ const ModalEvent2023FallItem = ({
isDisplayRandomBox ? (
<BodyRandomBox isBoxOpend={false} />
) : (
<Loading />
<div css={{ textAlign: "center" }}>
<Loading />
</div>
)
) : (
<img
Expand Down
61 changes: 50 additions & 11 deletions src/components/ModalPopup/ModalEvent2023FallRandomBox.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
import { useCallback, useState } from "react";
import { memo, useCallback, useEffect, useState } from "react";

import type { EventItem } from "types/event2023fall";

import { useDelay, useDelayBoolean } from "hooks/useDelay";

import Button from "components/Button";
import DottedLine from "components/DottedLine";
import BodyRandomBox from "components/Event/BodyRandomBox";
import Loading from "components/Loading";
import Modal from "components/Modal";

import "./ModalEvent2023FallRandomBoxBackground.css";

import theme from "tools/theme";

import LocalAtmRoundedIcon from "@mui/icons-material/LocalAtmRounded";
import HelpCenterRoundedIcon from "@mui/icons-material/HelpCenterRounded";

const Background = () => (
<div css={{ position: "absolute", top: "20%", left: 0, bottom: 0, right: 0 }}>
Expand All @@ -18,14 +23,23 @@ const Background = () => (
</div>
);

type ModalEvent2023FallRandomBoxProps = Parameters<typeof Modal>[0] & {};
type ModalEvent2023FallRandomBoxProps = { item?: EventItem } & Parameters<
typeof Modal
>[0];

const ModalEvent2023FallRandomBox = ({
item,
...modalProps
}: ModalEvent2023FallRandomBoxProps) => {
const [isBoxOpend, setIsBoxOpend] = useState<boolean>(false);
const isDisplayRandomBox = !useDelayBoolean(!modalProps.isOpen, 500);
const isDisplayItemName = useDelay<boolean>(isBoxOpend, !isBoxOpend, 6000);
const onClickOk = useCallback(() => setIsBoxOpend(true), []);

useEffect(() => {
if (!modalProps.isOpen) setIsBoxOpend(false);
}, [modalProps.isOpen]);

const styleTitle = {
...theme.font18,
display: "flex",
Expand All @@ -50,29 +64,54 @@ const ModalEvent2023FallRandomBox = ({
{...modalProps}
>
<div css={styleTitle}>
<LocalAtmRoundedIcon style={styleIcon} />
<HelpCenterRoundedIcon style={styleIcon} />
랜덤박스 열기
</div>
<div css={styleText}>
랜덤박스를 획득했다. <b>상자</b> 또는 <b>열기</b> 버튼을 눌러 상자 안
상품을 확인해보자.
<b css={{ color: theme.purple }}>랜덤박스를 획득했어요.</b> <b>상자</b>{" "}
또는 <b>열기</b> 버튼을 눌러 상자 안 상품을 확인해세요!
</div>
<DottedLine />
<BodyRandomBox isBoxOpend={isBoxOpend} onClickBox={onClickOk} />
{isDisplayRandomBox ? (
<BodyRandomBox
itemImageUrl={item?.imageUrl}
isBoxOpend={isBoxOpend}
onClickBox={onClickOk}
/>
) : (
<div css={{ textAlign: "center" }}>
<Loading />
</div>
)}
{isDisplayItemName && (
<div css={styleText}>
축하합니다! 랜덤박스에서{" "}
<b>
{'"'}
{item?.name || ""}
{'"'}
</b>
을(를) 획득하였습니다
</div>
)}
<Button
type="purple_inset"
css={{
padding: "10px 0 9px",
borderRadius: "8px",
...theme.font14_bold,
}}
disabled={isBoxOpend}
onClick={onClickOk}
disabled={isDisplayItemName ? false : isBoxOpend}
onClick={
isDisplayItemName
? () => modalProps?.onChangeIsOpen?.(false)
: onClickOk
}
>
박스 열기
{isDisplayItemName ? "확인" : "박스 열기"}
</Button>
</Modal>
);
};

export default ModalEvent2023FallRandomBox;
export default memo(ModalEvent2023FallRandomBox);
14 changes: 13 additions & 1 deletion src/pages/Event/Event2023FallStore/ItemListSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ import { useState } from "react";
import type { EventItem } from "types/event2023fall";

import Empty from "components/Empty";
import { ModalEvent2023FallItem } from "components/ModalPopup";
import {
ModalEvent2023FallItem,
ModalEvent2023FallRandomBox,
} from "components/ModalPopup";
import WhiteContainer from "components/WhiteContainer";

import theme from "tools/theme";
Expand All @@ -18,6 +21,7 @@ type EventItemComponentProps = {

const EventItemContainer = ({ value, fetchItems }: EventItemComponentProps) => {
const [isOpen, setIsOpen] = useState<boolean>(false);
const [rewardItem, setRewardItem] = useState<Nullable<EventItem>>(null);
const isSoldOut = value.stock <= 0;

return (
Expand Down Expand Up @@ -109,9 +113,17 @@ const EventItemContainer = ({ value, fetchItems }: EventItemComponentProps) => {
<ModalEvent2023FallItem
itemInfo={value}
fetchItems={fetchItems}
setRewardItem={setRewardItem}
isOpen={isOpen}
onChangeIsOpen={setIsOpen}
/>
{value.itemType === 3 && (
<ModalEvent2023FallRandomBox
isOpen={!!rewardItem}
onChangeIsOpen={() => setRewardItem(null)}
14KGun marked this conversation as resolved.
Show resolved Hide resolved
item={rewardItem || undefined}
/>
)}
</WhiteContainer>
);
};
Expand Down
Loading