diff --git a/CHANGELOG.md b/CHANGELOG.md index 14b4c75..d2b2336 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,11 @@ This file is automatically generated and maintained by the centralized workflow # Chuseok22 Version Changelog +## [0.4.1] - 2026-01-02 + +๐Ÿ› **patch**: ํ•˜๋‹จ ๋„ค๋น„๊ฒŒ์ด์…˜์„ ํ†ตํ•œ ํŽ˜์ด์ง€ ์ด๋™ ์‹œ replace ๋™์ž‘ +- commit: `05a52d7` + ## [0.4.0] - 2026-01-02 โœจ **minor**: ํ•™์ƒํšŒ๊ด€ ๋ฉ”๋‰ด ํŽ˜์ด์ง€ ์žฅ๋ฐ”๊ตฌ๋‹ˆ ์ถ”๊ฐ€ ๊ธฐ๋Šฅ ๊ฐœ๋ฐœ diff --git a/package-lock.json b/package-lock.json index 530f9c2..f76c80c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "campus-table-fe", - "version": "0.4.0", + "version": "0.4.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "campus-table-fe", - "version": "0.4.0", + "version": "0.4.1", "dependencies": { "@tanstack/react-query": "^5.90.14", "ioredis": "^5.8.2", diff --git a/package.json b/package.json index 0ac2eae..d7b242a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "campus-table-fe", - "version": "0.4.0", + "version": "0.4.1", "private": true, "scripts": { "dev": "next dev", diff --git a/src/constants/version.ts b/src/constants/version.ts index e517b5e..128bc0f 100644 --- a/src/constants/version.ts +++ b/src/constants/version.ts @@ -1 +1 @@ -export const APP_VERSION: string = '0.4.0'; +export const APP_VERSION: string = '0.4.1'; diff --git a/src/features/menu/components/MenuPageContainer.tsx b/src/features/menu/components/MenuPageContainer.tsx index 75e1652..2437067 100644 --- a/src/features/menu/components/MenuPageContainer.tsx +++ b/src/features/menu/components/MenuPageContainer.tsx @@ -10,6 +10,7 @@ import { useCart } from "@/features/cart/hooks/useCart"; import OrderSummaryBar from "@/features/menu/components/bar/OrderSummaryBar"; import { ItemCount } from "@/features/menu/components/button/CartButton"; import CartToast from "@/features/menu/components/toast/CartToast"; +import { useToast } from "@/shared/hooks/useToast"; interface MenuPageContainerProps { hakgwanMenuData: HakgwanMenuData; @@ -26,8 +27,7 @@ export default function MenuPageContainer({ return 0; }); - const [toastVisible, setToastVisible] = useState(false); - const [toastMessage, setToastMessage] = useState(""); + const { visible: toastVisible, message: toastMessage, showToast } = useToast(3000); const { cartInfo, addToCart } = useCart(); @@ -47,18 +47,10 @@ export default function MenuPageContainer({ const handleAddToCart = (menuId: number): void => { addToCart(menuId, { onSuccess: () => { - setToastMessage("์žฅ๋ฐ”๊ตฌ๋‹ˆ์— ์™ ๋‹ด์•˜์–ด์š”!"); - setToastVisible(true); - setTimeout(() => { - setToastVisible(false); - }, 3000); + showToast("์žฅ๋ฐ”๊ตฌ๋‹ˆ์— ์™ ๋‹ด์•˜์–ด์š”!"); }, onError: (message) => { - setToastMessage(message); - setToastVisible(true); - setTimeout(() => { - setToastVisible(false); - }, 3000); + showToast(message); } }); }; diff --git a/src/shared/hooks/useToast.ts b/src/shared/hooks/useToast.ts new file mode 100644 index 0000000..89c43b0 --- /dev/null +++ b/src/shared/hooks/useToast.ts @@ -0,0 +1,83 @@ +"use client"; + +import { useCallback, useEffect, useRef, useState } from "react"; + +interface UseToastReturn { + visible: boolean; + message: string; + showToast: (message: string) => void; +} + +/** + * ํ† ์ŠคํŠธ ๋ฉ”์‹œ์ง€ ๊ด€๋ฆฌ ํ›… + * - ํƒ€์ด๋จธ ์ถฉ๋Œ ๋ฐฉ์ง€ + * - ๊ธฐ์กด ํ† ์ŠคํŠธ๊ฐ€ ์žˆ์œผ๋ฉด ์ƒˆ๋กœ์šด ํ† ์ŠคํŠธ๋กœ ๊ต์ฒด + */ +export function useToast( + duration: number = 3000, + animationDuration: number = 200, +): UseToastReturn { + const [visible, setVisible] = useState(false); + const [message, setMessage] = useState(""); + const timerRef = useRef(null); + const hideTimerRef = useRef(null); + const visibleRef = useRef(false); + + const showToast = useCallback((message: string) => { + // ๊ธฐ์กด ํƒ€์ด๋จธ ์ •๋ฆฌ + if (timerRef.current) { + window.clearTimeout(timerRef.current); + } + if (hideTimerRef.current) { + window.clearTimeout(hideTimerRef.current); + } + + // ์ด๋ฏธ ํ† ์ŠคํŠธ๊ฐ€ ํ‘œ์‹œ์ค‘์ด๋ฉด ๋จผ์ € ์ œ๊ฑฐ + if (visibleRef.current) { + // ๊ธฐ์กด ํ† ์ŠคํŠธ ์ˆจ๊น€ + setVisible(false); + visibleRef.current = false; + + // ์• ๋‹ˆ๋ฉ”์ด์…˜ ์™„๋ฃŒ ๋Œ€๊ธฐ + hideTimerRef.current = window.setTimeout(() => { + setMessage(message); + setVisible(true); + visibleRef.current = true; + + // duration ํ›„ ํ† ์ŠคํŠธ ์ˆจ๊น€ + timerRef.current = window.setTimeout(() => { + setVisible(false); + visibleRef.current = false; + timerRef.current = null; + }, duration); + + hideTimerRef.current = null; + }, animationDuration); + } else { + // ๊ธฐ์กด ํ† ์ŠคํŠธ๊ฐ€ ์—†๋Š” ๊ฒฝ์šฐ + setMessage(message); + setVisible(true); + visibleRef.current = true; + + timerRef.current = window.setTimeout(() => { + setVisible(false); + visibleRef.current = false; + timerRef.current = null; + }, duration); + } + }, [duration, animationDuration]); + + // ์ปดํฌ๋„ŒํŠธ ์–ธ๋งˆ์šดํŠธ ์‹œ ํƒ€์ด๋จธ ์ •๋ฆฌ + useEffect(() => { + return () => { + if (timerRef.current) { + window.clearTimeout(timerRef.current); + } + if (hideTimerRef.current) { + window.clearTimeout(hideTimerRef.current); + } + }; + }, []); + + return { visible, message, showToast }; +} \ No newline at end of file