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

[Feat] Navigation Block 팝업 커스텀 구현 #193

Merged
merged 7 commits into from
Aug 22, 2024
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
10 changes: 10 additions & 0 deletions client/src/components/NavigationConfirmPopup/index.style.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { cva } from "class-variance-authority";

export const buttonStyles = cva("flex-1 py-400 rounded-1000 h-body-1-regular transition-all", {
variants: {
variant: {
primary: "bg-s-blue border border-s-blue text-n-white hover:bg-s-hover",
secondary: "bg-n-white border border-s-blue text-s-blue hover:bg-neutral-100",
},
},
});
48 changes: 48 additions & 0 deletions client/src/components/NavigationConfirmPopup/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { useEffect } from "react";
import { buttonStyles } from "./index.style";

interface NavigationConfirmPopUpProps {
handleConfirm: () => void;
handleClose: () => void;
}

export default function NavigationConfirmPopUp({
handleConfirm,
handleClose,
}: NavigationConfirmPopUpProps) {
useEffect(() => {
document.body.style.overflow = "hidden";

return () => {
document.body.style.overflow = "unset";
};
}, []);

return (
<div className="fixed w-full h-full left-0 top-0 z-20">
<div
className="absolute left-0 top-0 w-[100%] h-[100%] bg-n-black/[.4]"
onClick={handleClose}
/>
<div className="px-[50px] py-1000 bg-n-white rounded-600 absolute left-[50%] top-[50%] translate-y-[-50%] translate-x-[-50%]">
<p className="h-body-1-regular">
이 페이지를 떠나면 모든 변경 사항이 저장되지 않습니다.
<br />
페이지를 떠나시겠습니까?
</p>

<div className="flex gap-500 mt-800">
<button className={buttonStyles({ variant: "primary" })} onClick={handleClose}>
아니요
</button>
<button
className={buttonStyles({ variant: "secondary" })}
onClick={handleConfirm}
>
</button>
</div>
</div>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useState } from "react";
import type { Meta } from "@storybook/react";
import Component, { PopUpProps } from "./index";
import Component, { PhoneNumberPopUpProps } from "./index";

const meta = {
title: "PopUp",
Expand All @@ -15,7 +15,7 @@ const meta = {

export default meta;

const PopUp = (args: PopUpProps) => {
const PopUp = (args: PhoneNumberPopUpProps) => {
const [phoneNumber, setPhoneNumber] = useState(args.phoneNumber);

const handlePhoneNumberChange = (val: string) => {
Expand All @@ -32,7 +32,7 @@ const PopUp = (args: PopUpProps) => {
);
};

export const Default = (args: PopUpProps) => (
export const Default = (args: PhoneNumberPopUpProps) => (
<>
<PopUp {...args} />
</>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,19 @@ import CTAButton from "../CTAButton";
import CheckBox from "../CheckBox";
import Input from "../Input";

export interface PopUpProps {
export interface PhoneNumberPopUpProps {
phoneNumber: string;
handlePhoneNumberChange: (val: string) => void;
handlePhoneNumberConfirm: (val: string) => void;
handleClose: () => void;
}

export default function PopUp({
export default function PhoneNumberPopUp({
phoneNumber = "",
handlePhoneNumberChange,
handlePhoneNumberConfirm,
handleClose,
}: PopUpProps) {
}: PhoneNumberPopUpProps) {
const [isUserInfoCheck, setIsUserInfoCheck] = useState(true);
const [isMarketingInfoCheck, setIsMarketingInfoCheck] = useState(true);
const [canConfirm, setCanConfirm] = useState(false);
Expand Down
4 changes: 2 additions & 2 deletions client/src/features/Rush/Common/Headline.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import CTAButton from "@/components/CTAButton";
import Scroll from "@/components/Scroll";
import { ASCEND, ASCEND_DESCEND, SCROLL_MOTION } from "@/constants/animation.ts";
import { useAuth } from "@/hooks/useAuth.ts";
import usePopup from "@/hooks/usePopup.tsx";
import usePhoneNumberPopUp from "@/hooks/usePhoneNumberPopup";
import useToast from "@/hooks/useToast.tsx";
import { GetTotalRushEventsResponse } from "@/types/rushApi.ts";
import { SectionKeyProps } from "@/types/sections.ts";
Expand All @@ -21,7 +21,7 @@ export function Headline({ id, handleClickScroll }: HeadlineProps) {
const { phoneNumberState, handlePhoneNumberChange, handlePhoneNumberConfirm } =
useAuth("/rush/game");

const { handleOpenPopup, PopupComponent } = usePopup({
const { handleOpenPopup, PopupComponent } = usePhoneNumberPopUp({
phoneNumber: phoneNumberState,
handlePhoneNumberChange,
handlePhoneNumberConfirm,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,13 @@ function getWinStatus(ratio: number, oppositeRatio: number): WinStatus {
return ratio > oppositeRatio ? WIN_STATUS.WIN : WIN_STATUS.LOSE;
}

interface FinalResultProps {
unblockNavigation: () => void;
}

export default function FinalResult({ unblockNavigation }: FinalResultProps) {
export default function FinalResult() {
const [cookies] = useCookies([COOKIE_KEY.ACCESS_TOKEN]);
const { cardOptions, userParticipatedStatus, userSelectedOption } = useRushGameStateContext();
const { getRushResult, resultData } = useFetchRushResult();

useEffect(() => {
getRushResult(cookies[COOKIE_KEY.ACCESS_TOKEN]);
unblockNavigation();
}, []);

const isWinner = resultData?.isWinner;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,7 @@ function SelectedCardCurrentRatio({ onClick }: SelectedCardDetailsProps) {
);
}

interface SelectedCardProps {
unblockNavigation: () => void;
}

export default function SelectedCard({ unblockNavigation }: SelectedCardProps) {
export default function SelectedCard() {
const { toggleContents, toggle } = useToggleContents({ useDuration: false });
const fetchRushBalance = useFetchRushBalance();

Expand All @@ -67,7 +63,6 @@ export default function SelectedCard({ unblockNavigation }: SelectedCardProps) {

useEffect(() => {
fetchRushBalance();
unblockNavigation();
}, []);

return (
Expand Down
22 changes: 18 additions & 4 deletions client/src/hooks/useBlockNavigation.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,29 @@
import { useEffect, useState } from "react";
import { unstable_usePrompt, useLocation } from "react-router-dom";
import { useBlocker, useLocation } from "react-router-dom";

export function useBlockNavigation(message: string) {
export function useBlockNavigation() {
const location = useLocation();
const [isBlocking, setIsBlocking] = useState(false);

unstable_usePrompt({ when: isBlocking, message });
const blocker = useBlocker(isBlocking);
const isBlockedNavigation = blocker.state === "blocked";

const unblockNavigation = () => {
setIsBlocking(false);
};

const cancelNavigation = () => {
if (blocker.state === "blocked") {
blocker.reset();
}
};

const proceedNavigation = () => {
if (blocker.state === "blocked") {
blocker.proceed();
}
};

const handleBeforeUnload = (e: BeforeUnloadEvent) => {
if (isBlocking) {
e.preventDefault();
Expand All @@ -25,6 +38,7 @@ export function useBlockNavigation(message: string) {
setIsBlocking(false);
};
}, [location]);

useEffect(() => {
window.addEventListener("beforeunload", handleBeforeUnload);

Expand All @@ -33,5 +47,5 @@ export function useBlockNavigation(message: string) {
};
}, [isBlocking]);

return { unblockNavigation };
return { unblockNavigation, isBlockedNavigation, proceedNavigation, cancelNavigation };
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { useEffect, useState } from "react";
import Popup, { PopUpProps } from "@/components/PopUp";
import PhoneNumberPopUp, { PhoneNumberPopUpProps } from "@/components/PhoneNumberPopUp";

export default function usePopup({
export default function usePhoneNumberPopUp({
phoneNumber,
handlePhoneNumberChange,
handlePhoneNumberConfirm,
}: Omit<PopUpProps, "handleClose">) {
}: Omit<PhoneNumberPopUpProps, "handleClose">) {
const [isVisible, setIsVisible] = useState(false);

useEffect(() => {
Expand All @@ -26,7 +26,7 @@ export default function usePopup({
};

const PopupComponent = isVisible ? (
<Popup
<PhoneNumberPopUp
phoneNumber={phoneNumber}
handlePhoneNumberChange={handlePhoneNumberChange}
handlePhoneNumberConfirm={handlePhoneNumberConfirm}
Expand Down
13 changes: 10 additions & 3 deletions client/src/pages/CasperCustom/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useState } from "react";
import { motion } from "framer-motion";
import NavigationConfirmPopUp from "@/components/NavigationConfirmPopup";
import {
CUSTOM_STEP_HEADLINE,
CUSTOM_STEP_OPTION,
Expand All @@ -21,9 +22,8 @@ import useHeaderStyleObserver from "@/hooks/useHeaderStyleObserver";
const INITIAL_STEP = 0;

export default function CasperCustom() {
const { unblockNavigation } = useBlockNavigation(
"이 페이지를 떠나면 모든 변경 사항이 저장되지 않습니다. 페이지를 떠나시겠습니까?"
);
const { unblockNavigation, isBlockedNavigation, proceedNavigation, cancelNavigation } =
useBlockNavigation();

const containerRef = useHeaderStyleObserver({
darkSections: [CASPER_CUSTOM_SECTIONS.CUSTOM],
Expand Down Expand Up @@ -82,6 +82,13 @@ export default function CasperCustom() {
{renderCustomStep()}
</section>
</div>

{isBlockedNavigation && (
<NavigationConfirmPopUp
handleConfirm={proceedNavigation}
handleClose={cancelNavigation}
/>
)}
</CasperCustomProvider>
);
}
4 changes: 2 additions & 2 deletions client/src/pages/Lottery/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
} from "@/features/Lottery";
import { useAuth } from "@/hooks/useAuth.ts";
import useHeaderStyleObserver from "@/hooks/useHeaderStyleObserver.ts";
import usePopup from "@/hooks/usePopup";
import usePhoneNumberPopUp from "@/hooks/usePhoneNumberPopup";
import useScrollToTarget from "@/hooks/useScrollToTarget";
import useScrollTop from "@/hooks/useScrollTop";
import useToast from "@/hooks/useToast";
Expand All @@ -36,7 +36,7 @@ export default function Lottery() {
const { phoneNumberState, handlePhoneNumberChange, handlePhoneNumberConfirm } =
useAuth("/lottery/custom");

const { handleOpenPopup, PopupComponent } = usePopup({
const { handleOpenPopup, PopupComponent } = usePhoneNumberPopUp({
phoneNumber: phoneNumberState,
handlePhoneNumberChange,
handlePhoneNumberConfirm,
Expand Down
8 changes: 2 additions & 6 deletions client/src/pages/RushGame/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,10 @@ import useRushGameStateContext from "@/hooks/Contexts/useRushGameStateContext.ts
import { useFetchRushUserParticipationStatus } from "@/hooks/RushGame/useFetchRushUserParticipationStatus.ts";
import { useFetchTodayRushEvent } from "@/hooks/RushGame/useFetchTodayRushEvent.ts";
import useSetGamePhase from "@/hooks/RushGame/useSetGamePhase.ts";
import { useBlockNavigation } from "@/hooks/useBlockNavigation.ts";
import { GetTotalRushEventsResponse } from "@/types/rushApi.ts";

export default function RushGame() {
const [cookies] = useCookies([COOKIE_KEY.ACCESS_TOKEN]);
const { unblockNavigation } = useBlockNavigation(
"이 페이지를 떠나면 모든 변경 사항이 저장되지 않습니다. 페이지를 떠나시겠습니까?"
);
const { getTodayRushEvent } = useFetchTodayRushEvent();
const gameState = useRushGameStateContext();
const { getRushUserParticipationStatus, userParticipatedStatus } =
Expand All @@ -43,11 +39,11 @@ export default function RushGame() {
if (!gameState.userParticipatedStatus) {
return <CardOptions />;
} else {
return <SelectedCard unblockNavigation={unblockNavigation} />;
return <SelectedCard />;
}
}
case CARD_PHASE.COMPLETED:
return <FinalResult unblockNavigation={unblockNavigation} />;
return <FinalResult />;
default:
return null;
}
Expand Down
Loading