diff --git a/src/components/SyncSettings.tsx b/src/components/SyncSettings.tsx index 5137604..43cc9a6 100644 --- a/src/components/SyncSettings.tsx +++ b/src/components/SyncSettings.tsx @@ -1,3 +1,4 @@ + import React, { useState, useEffect } from 'react'; import { Switch } from "@/components/ui/switch"; import { Label } from "@/components/ui/label"; @@ -16,21 +17,41 @@ const SyncSettings = () => { const { user } = useAuth(); const navigate = useNavigate(); + // 사용자 로그인 상태 변경 감지 useEffect(() => { - // 마지막 동기화 시간 업데이트 - setLastSync(getLastSyncTime()); - - // 세션 상태 변경 감지 - const handleAuthChange = () => { + // 사용자 로그인 상태에 따라 동기화 설정 업데이트 + const updateSyncState = () => { + if (!user && isSyncEnabled()) { + // 사용자가 로그아웃했고 동기화가 활성화되어 있으면 비활성화 + setSyncEnabled(false); + setEnabled(false); + setLastSync(null); + console.log('로그아웃으로 인해 동기화 설정이 비활성화되었습니다.'); + } + + // 동기화 상태 업데이트 setEnabled(isSyncEnabled()); setLastSync(getLastSyncTime()); }; + + // 초기 호출 + updateSyncState(); - window.addEventListener('auth-state-changed', handleAuthChange); + // 인증 상태 변경 이벤트 리스너 + window.addEventListener('auth-state-changed', updateSyncState); return () => { - window.removeEventListener('auth-state-changed', handleAuthChange); + window.removeEventListener('auth-state-changed', updateSyncState); }; + }, [user]); + + // 마지막 동기화 시간 정기적으로 업데이트 + useEffect(() => { + const intervalId = setInterval(() => { + setLastSync(getLastSyncTime()); + }, 10000); // 10초마다 업데이트 + + return () => clearInterval(intervalId); }, []); const handleSyncToggle = async (checked: boolean) => { @@ -171,6 +192,7 @@ const SyncSettings = () => { checked={enabled} onCheckedChange={handleSyncToggle} className="data-[state=checked]:bg-neuro-income" + disabled={!user && enabled} // 사용자가 로그아웃 상태이면서 동기화가 켜져있을 때 비활성화 /> diff --git a/src/contexts/auth/AuthProvider.tsx b/src/contexts/auth/AuthProvider.tsx index 3b8273f..d281ddc 100644 --- a/src/contexts/auth/AuthProvider.tsx +++ b/src/contexts/auth/AuthProvider.tsx @@ -5,6 +5,7 @@ import { Session, User } from '@supabase/supabase-js'; import { toast } from '@/hooks/useToast.wrapper'; import { AuthContextType } from './types'; import * as authActions from './authActions'; +import { clearAllToasts } from '@/hooks/toast/toastManager'; const AuthContext = createContext(undefined); @@ -45,6 +46,12 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children } else if (event === 'SIGNED_OUT') { setSession(null); setUser(null); + + // 로그아웃 시 열려있는 모든 토스트 제거 + clearAllToasts(); + + // 로그아웃 이벤트 발생시켜 SyncSettings 등에서 감지하도록 함 + window.dispatchEvent(new Event('auth-state-changed')); } setLoading(false); diff --git a/src/hooks/toast/constants.ts b/src/hooks/toast/constants.ts index 3120789..9d0e167 100644 --- a/src/hooks/toast/constants.ts +++ b/src/hooks/toast/constants.ts @@ -1,3 +1,3 @@ export const TOAST_LIMIT = 5 // 최대 5개로 제한 -export const TOAST_REMOVE_DELAY = 5000 // 5초 후 DOM에서 제거 +export const TOAST_REMOVE_DELAY = 3000 // 3초 후 DOM에서 제거 (5초에서 3초로 변경) diff --git a/src/hooks/toast/index.ts b/src/hooks/toast/index.ts index e437ffe..5c84740 100644 --- a/src/hooks/toast/index.ts +++ b/src/hooks/toast/index.ts @@ -27,7 +27,7 @@ function toast({ ...props }: Toast) { onOpenChange: (open) => { if (!open) dismiss() }, - duration: props.duration || 3000, // 기본 지속 시간 3초로 단축 + duration: props.duration || 3000, // 기본 지속 시간 3초로 설정 }, }) diff --git a/src/hooks/toast/reducer.ts b/src/hooks/toast/reducer.ts index 8ca9c53..ef237fc 100644 --- a/src/hooks/toast/reducer.ts +++ b/src/hooks/toast/reducer.ts @@ -9,7 +9,9 @@ export const toastTimeouts = new Map>() // 토스트 자동 제거 함수 export const addToRemoveQueue = (toastId: string) => { if (toastTimeouts.has(toastId)) { - return + // 이미 존재하는 타이머가 있다면 제거 후 새로 설정 + clearTimeout(toastTimeouts.get(toastId)!); + toastTimeouts.delete(toastId); } const timeout = setTimeout(() => { @@ -26,6 +28,10 @@ export const addToRemoveQueue = (toastId: string) => { export const reducer = (state: State, action: Action): State => { switch (action.type) { case actionTypes.ADD_TOAST: + // 토스트 추가 시 자동 제거 타이머 설정 + if (action.toast.id) { + addToRemoveQueue(action.toast.id); + } return { ...state, toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT), diff --git a/src/hooks/toast/toastManager.ts b/src/hooks/toast/toastManager.ts index 030034c..e768a53 100644 --- a/src/hooks/toast/toastManager.ts +++ b/src/hooks/toast/toastManager.ts @@ -47,11 +47,26 @@ export function dispatch(action: Action) { time: now }; + // REMOVE_TOAST 액션 우선순위 높임 + if (action.type === actionTypes.REMOVE_TOAST) { + // 즉시 처리 + memoryState = reducer(memoryState, action); + listeners.forEach((listener) => { + listener(memoryState); + }); + return; + } + // 실제 상태 업데이트 및 리스너 호출 - memoryState = reducer(memoryState, action) + memoryState = reducer(memoryState, action); listeners.forEach((listener) => { - listener(memoryState) - }) + listener(memoryState); + }); +} + +// 토스트 모두 제거 헬퍼 함수 +export function clearAllToasts() { + dispatch({ type: actionTypes.REMOVE_TOAST }); } export { memoryState };