diff --git a/com.twin.app.shoptime/src/actions/actionTypes.js b/com.twin.app.shoptime/src/actions/actionTypes.js index a1d78cb1..88c57276 100644 --- a/com.twin.app.shoptime/src/actions/actionTypes.js +++ b/com.twin.app.shoptime/src/actions/actionTypes.js @@ -27,6 +27,10 @@ export const types = { SHOW_OPTIONAL_TERMS_CONFIRM_POPUP: "SHOW_OPTIONAL_TERMS_CONFIRM_POPUP", HIDE_OPTIONAL_TERMS_CONFIRM_POPUP: "HIDE_OPTIONAL_TERMS_CONFIRM_POPUP", TOGGLE_OPTIONAL_TERMS_CONFIRM: "TOGGLE_OPTIONAL_TERMS_CONFIRM", + // 선택약관 팝업 상태 관리 + SET_OPTIONAL_TERMS_POPUP_SHOWN: "SET_OPTIONAL_TERMS_POPUP_SHOWN", + SET_OPTIONAL_TERMS_USER_DECISION: "SET_OPTIONAL_TERMS_USER_DECISION", + RESET_OPTIONAL_TERMS_SESSION: "RESET_OPTIONAL_TERMS_SESSION", SET_EXIT_APP: "SET_EXIT_APP", GET_LOGIN_USER_DATA: "GET_LOGIN_USER_DATA", GET_TERMS_AGREE_YN: "GET_TERMS_AGREE_YN", diff --git a/com.twin.app.shoptime/src/actions/commonActions.js b/com.twin.app.shoptime/src/actions/commonActions.js index 01cdf5a5..d1ef7cdc 100644 --- a/com.twin.app.shoptime/src/actions/commonActions.js +++ b/com.twin.app.shoptime/src/actions/commonActions.js @@ -740,3 +740,32 @@ export const enableNotification = () => (dispatch, getState) => { }, }); }; + +// 선택약관 팝업 상태 관리 액션 생성자들 (TV 환경 최적화) +export const setOptionalTermsPopupShown = (shown = true) => ({ + type: types.SET_OPTIONAL_TERMS_POPUP_SHOWN, + payload: shown, +}); + +export const setOptionalTermsUserDecision = (decision) => ({ + type: types.SET_OPTIONAL_TERMS_USER_DECISION, + payload: decision, // 'agreed' | 'declined' | null +}); + +export const resetOptionalTermsSession = () => ({ + type: types.RESET_OPTIONAL_TERMS_SESSION, +}); + +// 선택약관 동의 처리를 위한 헬퍼 함수 +export const handleOptionalTermsAgree = () => (dispatch) => { + console.log('[CommonActions] 선택약관 동의 처리'); + dispatch(setOptionalTermsUserDecision('agreed')); + dispatch(setOptionalTermsPopupShown(true)); +}; + +// 선택약관 거절 처리를 위한 헬퍼 함수 +export const handleOptionalTermsDecline = () => (dispatch) => { + console.log('[CommonActions] 선택약관 거절 처리'); + dispatch(setOptionalTermsUserDecision('declined')); + dispatch(setOptionalTermsPopupShown(true)); +}; diff --git a/com.twin.app.shoptime/src/reducers/commonReducer.js b/com.twin.app.shoptime/src/reducers/commonReducer.js index e57f9b98..c3d9f1e9 100644 --- a/com.twin.app.shoptime/src/reducers/commonReducer.js +++ b/com.twin.app.shoptime/src/reducers/commonReducer.js @@ -81,7 +81,14 @@ const initialState = { MST00401: false, // 개인정보처리방침 (필수) MST00402: false, // 이용약관 (필수) MST00405: false, // 선택약관 (선택) - } + }, + + // 선택약관 팝업 상태 관리 (TV 환경 최적화) + optionalTermsPopupFlow: { + popupShown: false, // 팝업 표시 여부 + userDecision: null, // 'agreed' | 'declined' | null + agreedInSession: false, // 세션 내 동의 여부 (로컬 상태 기반) + }, }; export const commonReducer = (state = initialState, action) => { @@ -374,6 +381,39 @@ export const commonReducer = (state = initialState, action) => { }; } + // 선택약관 팝업 상태 관리 액션들 + case types.SET_OPTIONAL_TERMS_POPUP_SHOWN: { + return { + ...state, + optionalTermsPopupFlow: { + ...state.optionalTermsPopupFlow, + popupShown: action.payload, + }, + }; + } + + case types.SET_OPTIONAL_TERMS_USER_DECISION: { + return { + ...state, + optionalTermsPopupFlow: { + ...state.optionalTermsPopupFlow, + userDecision: action.payload, + agreedInSession: action.payload === 'agreed', + }, + }; + } + + case types.RESET_OPTIONAL_TERMS_SESSION: { + return { + ...state, + optionalTermsPopupFlow: { + popupShown: false, + userDecision: null, + agreedInSession: false, + }, + }; + } + default: return state; } diff --git a/com.twin.app.shoptime/src/views/HomePanel/HomeBanner/HomeBanner.jsx b/com.twin.app.shoptime/src/views/HomePanel/HomeBanner/HomeBanner.jsx index fa9a73fd..68c20841 100644 --- a/com.twin.app.shoptime/src/views/HomePanel/HomeBanner/HomeBanner.jsx +++ b/com.twin.app.shoptime/src/views/HomePanel/HomeBanner/HomeBanner.jsx @@ -12,7 +12,13 @@ import { // setShowPopup, fetchCurrentUserHomeTerms, } from "../../../actions/homeActions"; -import { changeAppStatus } from "../../../actions/commonActions"; +import { + changeAppStatus, + setOptionalTermsPopupShown, + setOptionalTermsUserDecision, + handleOptionalTermsAgree as handleOptionalTermsAgreeAction, + handleOptionalTermsDecline, +} from "../../../actions/commonActions"; import { setMyPageTermsAgree } from "../../../actions/myPageActions"; import { pushPanel, popPanel } from "../../../actions/panelActions"; import { panel_names } from "../../../utils/Config"; @@ -161,6 +167,9 @@ export default function HomeBanner({ // 선택약관 동의여부 const introTermsAgree = useSelector((state) => state.common.introTermsAgree); + // 새로운 Redux 상태: 선택약관 팝업 플로우 관리 + const optionalTermsPopupFlow = useSelector((state) => state.common.optionalTermsPopupFlow); + //------------------------------------------------------------------------------ // 팝업표시 상태 const [isOptionalConfirmVisible, setIsOptionalConfirmVisible] = @@ -174,17 +183,25 @@ export default function HomeBanner({ console.log('[HomeBanner] Step 1: 상태 확인', { termsLoading, isGnbOpened, - optionalTermsAgreed, - optionalTermsAvailable + optionalTermsAvailable, + optionalTermsPopupFlow }); - // optionalTermsAvailable = false면 팝업 표시 안함 - if (termsLoading || isGnbOpened || optionalTermsAgreed || !optionalTermsAvailable) { - console.log('[HomeBanner] Early return: 조건 불만족'); + // 1. 기본 조건 확인 + if (termsLoading || isGnbOpened || !optionalTermsAvailable) { + console.log('[HomeBanner] Early return: 기본 조건 불만족'); + return false; + } + + // 2. 새로운 Redux 상태 확인 (TV 환경 최적화) + if (optionalTermsPopupFlow.popupShown || + optionalTermsPopupFlow.userDecision || + optionalTermsPopupFlow.agreedInSession) { + console.log('[HomeBanner] Early return: 이미 처리됨', optionalTermsPopupFlow); return false; } - // Chromium68 호환성을 위해 Optional Chaining 제거 + // 3. 서버 데이터 확인 const terms = termsData && termsData.data && termsData.data.terms; console.log('[HomeBanner] Step 2: termsData 확인', terms); if (!terms) { @@ -201,7 +218,7 @@ export default function HomeBanner({ console.log('[HomeBanner] Step 4: 최종 결과', result); return result; - }, [termsData, termsLoading, isGnbOpened, optionalTermsAgreed, optionalTermsAvailable]); + }, [termsData, termsLoading, isGnbOpened, optionalTermsAvailable, optionalTermsPopupFlow]); // 선택약관 팝업 표시 여부 =================================================== @@ -254,7 +271,10 @@ export default function HomeBanner({ if (process.env.NODE_ENV === "development") { console.log("[HomeBanner] 약관 동의 성공:", response); } - // 약관 동의 성공 상태 설정 + // 새로운 Redux 액션을 사용하여 상태 업데이트 + dispatch(setOptionalTermsUserDecision('agreed')); + dispatch(setOptionalTermsPopupShown(true)); + // 로컬 상태도 업데이트 (기존 로직 유지) setOptionalTermsAgreed(true); // 약관 데이터 갱신 dispatch(fetchCurrentUserHomeTerms()); @@ -289,9 +309,11 @@ export default function HomeBanner({ const handleOptionalDeclineClick = useCallback(() => { console.log("[HomeBanner] 거절/다음에 하기 버튼 클릭"); + // 새로운 Redux 액션을 사용하여 거절 상태 업데이트 + dispatch(setOptionalTermsUserDecision('declined')); + dispatch(setOptionalTermsPopupShown(true)); setIsOptionalConfirmVisible(false); - // 거절 처리 로직 추가 - }, []); + }, [dispatch]); // 선택약관 팝업 Close const handleTermsPopupClosed = useCallback(() => { @@ -365,26 +387,28 @@ export default function HomeBanner({ // }, 1000); // }, []); - // 약관 동의 및 선택 약관 팝업 처리 + // 약관 동의 및 선택 약관 팝업 처리 (TV 환경 최적화) useEffect(() => { if (termsLoading) { // 약관 데이터 로딩 중에는 아무것도 하지 않음 return; } + // 선택 약관 팝업을 띄워야 하는 경우 - if (shouldShowOptionalTermsPopup) { - // 3초 후에 팝업을 띄우도록 설정 + if (shouldShowOptionalTermsPopup && !isOptionalConfirmVisible) { console.log("shouldShowOptionalTermsPopup", shouldShowOptionalTermsPopup); console.log("App.js optionalTermsConfirm 팝업 표시"); + const timer = setTimeout(() => { setIsOptionalConfirmVisible(true); - // dispatch(setShowPopup({ activePopup: "optionalTermsConfirm" })); - }, 1000); // 3000 milliseconds = 3 seconds + // 팝업이 실제로 표시된 후에 Redux 상태 업데이트 + dispatch(setOptionalTermsPopupShown(true)); + }, 1000); // 컴포넌트 언마운트 시 타이머 클리어 return () => clearTimeout(timer); } - }, [shouldShowOptionalTermsPopup, termsLoading]); + }, [shouldShowOptionalTermsPopup, termsLoading, isOptionalConfirmVisible, dispatch]); // const renderItem = useCallback( // (index, isHorizontal) => { diff --git a/com.twin.app.shoptime/src/views/IntroPanel/IntroPanel.new.jsx b/com.twin.app.shoptime/src/views/IntroPanel/IntroPanel.new.jsx index 1fdc13bc..7ba6cd1c 100644 --- a/com.twin.app.shoptime/src/views/IntroPanel/IntroPanel.new.jsx +++ b/com.twin.app.shoptime/src/views/IntroPanel/IntroPanel.new.jsx @@ -17,6 +17,9 @@ import { setHidePopup, setShowPopup, setDeviceRegistered, + setOptionalTermsUserDecision, + setOptionalTermsPopupShown, + resetOptionalTermsSession, // setTermsAgreeYn, } from "../../actions/commonActions"; import { registerDevice } from "../../actions/deviceActions"; @@ -357,6 +360,23 @@ function IntroPanelWithOptional({ // ================== SUCCESS ================== if (newRegDeviceData && newRegDeviceData.retCode === 0) { dispatch(setDeviceRegistered(true)); + + // 선택약관 상태를 Redux에 업데이트 (TV 환경 최적화) + if (optionalChecked) { + // 선택약관에 동의한 경우 + console.log("[IntroPanel] 선택약관 동의됨 - Redux 상태 업데이트"); + dispatch(setOptionalTermsUserDecision('agreed')); + dispatch(setOptionalTermsPopupShown(true)); + } else { + // 선택약관에 동의하지 않은 경우 - HomeBanner에서 팝업이 나올 수 있도록 상태를 초기화 + console.log("[IntroPanel] 선택약관 미동의 - HomeBanner 팝업 허용을 위해 상태 초기화"); + // fetchCurrentUserHomeTerms 완료 후 Redux 상태를 리셋하여 HomeBanner에서 팝업 표시 조건을 정확히 평가할 수 있도록 함 + setTimeout(() => { + console.log("[IntroPanel] 약관 데이터 갱신 후 상태 리셋 실행"); + dispatch(resetOptionalTermsSession()); + }, 1000); // 약관 데이터 갱신 완료를 기다림 + } + dispatch( getWelcomeEventInfo((eventInfos) => { if (