From 8184915cb123d9ac128a29d9f116895efbae23f6 Mon Sep 17 00:00:00 2001 From: corpi Date: Sat, 7 Mar 2026 18:37:05 +0900 Subject: [PATCH] =?UTF-8?q?fix(space-focus):=20=EB=AA=A9=ED=91=9C=20?= =?UTF-8?q?=EC=95=88=EB=82=B4=EB=A5=BC=20=EC=83=81=EB=8B=A8=20=ED=86=A0?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=EB=A1=9C=20=ED=86=B5=ED=95=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 맥락: - space 진입 직후 목표를 확정하면 하단 HUD 위에 GoalFlashOverlay가 노출되어 상단 토스트 흐름과 UI 기준점이 분리되어 있었다. 변경사항: - GoalFlashOverlay 컴포넌트를 제거했다. - SpaceFocusHudWidget에서 집중 진입 시점과 paused -> running 복귀 시점의 목표 안내를 onStatusMessage로 올리도록 변경했다. - 목표 안내가 기존 FocusTopToast를 통해 상단에서 일관되게 보이도록 정리했다. 검증: - npm run lint 세션-상태: 목표 안내가 하단 오버레이 없이 상단 토스트만 사용하는 상태 세션-다음: 실제 사운드/배경 적용 시 상단 상태 메시지 우선순위를 함께 점검 세션-리스크: 연속 상태 메시지가 짧은 간격으로 발생하면 토스트 큐 길이에 따라 일부 메시지가 뒤로 밀릴 수 있음 --- .../space-focus-hud/ui/GoalFlashOverlay.tsx | 25 ------ .../ui/SpaceFocusHudWidget.tsx | 83 +++---------------- 2 files changed, 13 insertions(+), 95 deletions(-) delete mode 100644 src/widgets/space-focus-hud/ui/GoalFlashOverlay.tsx diff --git a/src/widgets/space-focus-hud/ui/GoalFlashOverlay.tsx b/src/widgets/space-focus-hud/ui/GoalFlashOverlay.tsx deleted file mode 100644 index 02e008f..0000000 --- a/src/widgets/space-focus-hud/ui/GoalFlashOverlay.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { cn } from '@/shared/lib/cn'; - -interface GoalFlashOverlayProps { - goal: string; - visible: boolean; -} - -export const GoalFlashOverlay = ({ goal, visible }: GoalFlashOverlayProps) => { - const normalizedGoal = goal.trim().length > 0 ? goal.trim() : '이번 한 조각을 잊지 마세요.'; - - return ( -
-
- 이번 한 조각: {normalizedGoal} -
-
- ); -}; diff --git a/src/widgets/space-focus-hud/ui/SpaceFocusHudWidget.tsx b/src/widgets/space-focus-hud/ui/SpaceFocusHudWidget.tsx index acac19e..3b66a77 100644 --- a/src/widgets/space-focus-hud/ui/SpaceFocusHudWidget.tsx +++ b/src/widgets/space-focus-hud/ui/SpaceFocusHudWidget.tsx @@ -1,9 +1,7 @@ -import { useCallback, useEffect, useRef, useState } from 'react'; +import { useEffect, useRef, useState } from 'react'; import type { HudStatusLinePayload } from '@/shared/lib/useHudStatusLine'; -import { useReducedMotion } from '@/shared/lib/useReducedMotion'; import { SpaceTimerHudWidget } from '@/widgets/space-timer-hud'; import { GoalCompleteSheet } from './GoalCompleteSheet'; -import { GoalFlashOverlay } from './GoalFlashOverlay'; interface SpaceFocusHudWidgetProps { goal: string; @@ -34,36 +32,14 @@ export const SpaceFocusHudWidget = ({ onGoalUpdate, onStatusMessage, }: SpaceFocusHudWidgetProps) => { - const reducedMotion = useReducedMotion(); - const [flashVisible, setFlashVisible] = useState(false); const [sheetOpen, setSheetOpen] = useState(false); + const visibleRef = useRef(false); const playbackStateRef = useRef<'running' | 'paused'>(playbackState); - const flashTimerRef = useRef(null); const restReminderTimerRef = useRef(null); - - const triggerFlash = useCallback((durationMs: number) => { - if (reducedMotion || !visible) { - return; - } - - setFlashVisible(true); - - if (flashTimerRef.current) { - window.clearTimeout(flashTimerRef.current); - } - - flashTimerRef.current = window.setTimeout(() => { - setFlashVisible(false); - flashTimerRef.current = null; - }, durationMs); - }, [reducedMotion, visible]); + const normalizedGoal = goal.trim().length > 0 ? goal.trim() : '집중을 시작해요.'; useEffect(() => { return () => { - if (flashTimerRef.current) { - window.clearTimeout(flashTimerRef.current); - flashTimerRef.current = null; - } if (restReminderTimerRef.current) { window.clearTimeout(restReminderTimerRef.current); restReminderTimerRef.current = null; @@ -72,56 +48,24 @@ export const SpaceFocusHudWidget = ({ }, []); useEffect(() => { - if (!visible || reducedMotion) { - const rafId = window.requestAnimationFrame(() => { - setFlashVisible(false); + if (visible && !visibleRef.current) { + onStatusMessage({ + message: `이번 한 조각 · ${normalizedGoal}`, }); - - return () => { - window.cancelAnimationFrame(rafId); - }; } - const timeoutId = window.setTimeout(() => { - triggerFlash(2000); - }, 0); - - return () => { - window.clearTimeout(timeoutId); - }; - }, [visible, reducedMotion, triggerFlash]); + visibleRef.current = visible; + }, [normalizedGoal, onStatusMessage, visible]); useEffect(() => { - if (playbackStateRef.current === 'paused' && playbackState === 'running' && visible && !reducedMotion) { - const timeoutId = window.setTimeout(() => { - triggerFlash(1000); - }, 0); - - playbackStateRef.current = playbackState; - - return () => { - window.clearTimeout(timeoutId); - }; + if (playbackStateRef.current === 'paused' && playbackState === 'running' && visible) { + onStatusMessage({ + message: `이번 한 조각 · ${normalizedGoal}`, + }); } playbackStateRef.current = playbackState; - }, [playbackState, reducedMotion, triggerFlash, visible]); - - useEffect(() => { - const ENABLE_PERIODIC_FLASH = false; - - if (!visible || reducedMotion || !ENABLE_PERIODIC_FLASH) { - return; - } - - const intervalId = window.setInterval(() => { - triggerFlash(800); - }, 10 * 60 * 1000); - - return () => { - window.clearInterval(intervalId); - }; - }, [visible, reducedMotion, triggerFlash]); + }, [normalizedGoal, onStatusMessage, playbackState, visible]); if (!visible) { return null; @@ -133,7 +77,6 @@ export const SpaceFocusHudWidget = ({ return ( <> -