fix: 선택약관 4차수정 250622
This commit is contained in:
@@ -1,28 +1,41 @@
|
|||||||
import React, { useCallback, useEffect, useMemo, useRef } from "react";
|
import React, {
|
||||||
|
// useMemo,
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
useCallback,
|
||||||
|
useEffect,
|
||||||
import platform from "@enact/core/platform";
|
useRef,
|
||||||
import { Job } from "@enact/core/util";
|
// useState,
|
||||||
import ThemeDecorator from "@enact/sandstone/ThemeDecorator";
|
} from "react";
|
||||||
|
import { useSelector, useDispatch } from "react-redux";
|
||||||
|
// import { I18nContext } from "@enact/i18n";
|
||||||
|
// import classNames from "classnames";
|
||||||
|
// import PropTypes from "prop-types";
|
||||||
import Spotlight from "@enact/spotlight";
|
import Spotlight from "@enact/spotlight";
|
||||||
|
import { Job } from "@enact/core/util";
|
||||||
|
import platform from "@enact/core/platform";
|
||||||
|
import { ThemeDecorator } from "@enact/sandstone/ThemeDecorator";
|
||||||
|
|
||||||
|
// import "../../../assets/fontello/css/fontello.css";
|
||||||
|
|
||||||
import appinfo from "../../webos-meta/appinfo.json";
|
|
||||||
import {
|
import {
|
||||||
changeAppStatus,
|
changeAppStatus,
|
||||||
checkFirstLaunch,
|
// cancelFocusElement,
|
||||||
|
// focusElement,
|
||||||
|
// setExitApp,
|
||||||
|
// setPreventMouse,
|
||||||
|
// setShowPopup,
|
||||||
|
// setTermsAgreeYn,
|
||||||
|
getTermsAgreeYn,
|
||||||
deleteOldDb8Datas,
|
deleteOldDb8Datas,
|
||||||
getConnectionInfo,
|
sendBroadCast,
|
||||||
getConnectionStatus,
|
getConnectionStatus,
|
||||||
|
getConnectionInfo,
|
||||||
getDeviceId,
|
getDeviceId,
|
||||||
getHttpHeaderForServiceRequest,
|
getHttpHeaderForServiceRequest,
|
||||||
getSystemSettings,
|
getSystemSettings,
|
||||||
sendBroadCast,
|
checkFirstLaunch,
|
||||||
setDeepLink,
|
setDeepLink,
|
||||||
setGNBMenu,
|
setGNBMenu,
|
||||||
setSecondLayerInfo,
|
setSecondLayerInfo,
|
||||||
setShowPopup,
|
|
||||||
getTermsAgreeYn,
|
|
||||||
} from "../actions/commonActions";
|
} from "../actions/commonActions";
|
||||||
import { getShoptimeTerms } from "../actions/empActions";
|
import { getShoptimeTerms } from "../actions/empActions";
|
||||||
import { getHomeMenu, getHomeTerms } from "../actions/homeActions";
|
import { getHomeMenu, getHomeTerms } from "../actions/homeActions";
|
||||||
@@ -36,7 +49,14 @@ import usePrevious from "../hooks/usePrevious";
|
|||||||
import { lunaTest } from "../lunaSend/lunaTest";
|
import { lunaTest } from "../lunaSend/lunaTest";
|
||||||
import { store } from "../store/store";
|
import { store } from "../store/store";
|
||||||
import * as Config from "../utils/Config";
|
import * as Config from "../utils/Config";
|
||||||
import { $L, clearLaunchParams, getLaunchParams } from "../utils/helperMethods";
|
import {
|
||||||
|
// $L,
|
||||||
|
clearLaunchParams,
|
||||||
|
// getCountry,
|
||||||
|
getLaunchParams,
|
||||||
|
// getUUID,
|
||||||
|
// resizeTo,
|
||||||
|
} from "../utils/helperMethods";
|
||||||
import { SpotlightIds } from "../utils/SpotlightIds";
|
import { SpotlightIds } from "../utils/SpotlightIds";
|
||||||
import ErrorBoundary from "../views/ErrorBoundary";
|
import ErrorBoundary from "../views/ErrorBoundary";
|
||||||
import MainView from "../views/MainView/MainView";
|
import MainView from "../views/MainView/MainView";
|
||||||
@@ -44,7 +64,11 @@ import css from "./App.module.less";
|
|||||||
import { handleBypassLink } from "./bypassLinkHandler";
|
import { handleBypassLink } from "./bypassLinkHandler";
|
||||||
import { handleDeepLink } from "./deepLinkHandler";
|
import { handleDeepLink } from "./deepLinkHandler";
|
||||||
import { sendLogTotalRecommend } from "../actions/logActions";
|
import { sendLogTotalRecommend } from "../actions/logActions";
|
||||||
import { startFocusMonitoring, stopFocusMonitoring } from '../utils/focus-monitor';
|
// import {
|
||||||
|
// startFocusMonitoring,
|
||||||
|
// stopFocusMonitoring,
|
||||||
|
// } from "../utils/focus-monitor";
|
||||||
|
// import { PanelHoc } from "../components/TPanel/TPanel";
|
||||||
|
|
||||||
let foreGroundChangeTimer = null;
|
let foreGroundChangeTimer = null;
|
||||||
|
|
||||||
@@ -80,7 +104,7 @@ Spotlight.focus = function (elem, containerOption) {
|
|||||||
if (!floatLayerNode.contains(current)) {
|
if (!floatLayerNode.contains(current)) {
|
||||||
if (floatLayerNode.lastElementChild) {
|
if (floatLayerNode.lastElementChild) {
|
||||||
const spottableNode = floatLayerNode.lastElementChild.querySelector(
|
const spottableNode = floatLayerNode.lastElementChild.querySelector(
|
||||||
'[data-spotlight-container="true"]'
|
'[data-spotlight-container="true"]',
|
||||||
);
|
);
|
||||||
if (spottableNode) {
|
if (spottableNode) {
|
||||||
originFocus.apply(this, [spottableNode]); // this 바인딩을 유지하여 originFocus 호출
|
originFocus.apply(this, [spottableNode]); // this 바인딩을 유지하여 originFocus 호출
|
||||||
@@ -98,7 +122,7 @@ Spotlight.focus = function (elem, containerOption) {
|
|||||||
sendBroadCast({
|
sendBroadCast({
|
||||||
type: "deActivateTab",
|
type: "deActivateTab",
|
||||||
moreInfo: { reason: "focus" },
|
moreInfo: { reason: "focus" },
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -111,15 +135,15 @@ function AppBase(props) {
|
|||||||
const httpHeader = useSelector((state) => state.common.httpHeader);
|
const httpHeader = useSelector((state) => state.common.httpHeader);
|
||||||
const httpHeaderRef = useRef(httpHeader);
|
const httpHeaderRef = useRef(httpHeader);
|
||||||
const webOSVersion = useSelector(
|
const webOSVersion = useSelector(
|
||||||
(state) => state.common.appStatus.webOSVersion
|
(state) => state.common.appStatus.webOSVersion,
|
||||||
);
|
);
|
||||||
const deviceId = useSelector((state) => state.common.appStatus.deviceId);
|
const deviceId = useSelector((state) => state.common.appStatus.deviceId);
|
||||||
const loginUserData = useSelector(
|
const loginUserData = useSelector(
|
||||||
(state) => state.common.appStatus.loginUserData
|
(state) => state.common.appStatus.loginUserData,
|
||||||
);
|
);
|
||||||
const loginUserDataRef = useRef(loginUserData);
|
const loginUserDataRef = useRef(loginUserData);
|
||||||
const cursorVisible = useSelector(
|
const cursorVisible = useSelector(
|
||||||
(state) => state.common.appStatus.cursorVisible
|
(state) => state.common.appStatus.cursorVisible,
|
||||||
);
|
);
|
||||||
const introTermsAgree = useSelector((state) => state.common.introTermsAgree);
|
const introTermsAgree = useSelector((state) => state.common.introTermsAgree);
|
||||||
// const optionalTermsAgree = useSelector((state) => state.common.optionalTermsAgree);
|
// const optionalTermsAgree = useSelector((state) => state.common.optionalTermsAgree);
|
||||||
@@ -128,15 +152,6 @@ function AppBase(props) {
|
|||||||
// const termsFlag = useSelector((state) => state.common.termsFlag);
|
// const termsFlag = useSelector((state) => state.common.termsFlag);
|
||||||
const termsData = useSelector((state) => state.home.termsData);
|
const termsData = useSelector((state) => state.home.termsData);
|
||||||
|
|
||||||
// const shouldShowOptionalTermsPopup = useMemo(() => {
|
|
||||||
// const terms = termsData?.data?.terms;
|
|
||||||
// if (!terms) {
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
// const optionalTerm = terms.find(term => term.trmsTpCd === "MST00405");
|
|
||||||
// return optionalTerm ? optionalTerm.trmsPopFlag === 'Y' && optionalTerm.trmsAgrFlag === 'N' : false;
|
|
||||||
// }, [termsData]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (termsData?.data?.terms) {
|
if (termsData?.data?.terms) {
|
||||||
dispatch(getTermsAgreeYn());
|
dispatch(getTermsAgreeYn());
|
||||||
@@ -146,9 +161,9 @@ function AppBase(props) {
|
|||||||
const introTermsAgreeRef = usePrevious(introTermsAgree);
|
const introTermsAgreeRef = usePrevious(introTermsAgree);
|
||||||
const logEnable = useSelector((state) => state.localSettings.logEnable);
|
const logEnable = useSelector((state) => state.localSettings.logEnable);
|
||||||
const oldDb8Deleted = useSelector(
|
const oldDb8Deleted = useSelector(
|
||||||
(state) => state.localSettings.oldDb8Deleted
|
(state) => state.localSettings.oldDb8Deleted,
|
||||||
);
|
);
|
||||||
const macAddress = useSelector((state) => state.common.macAddress);
|
// const macAddress = useSelector((state) => state.common.macAddress);
|
||||||
|
|
||||||
const deviceCountryCode = httpHeader?.["X-Device-Country"] || "";
|
const deviceCountryCode = httpHeader?.["X-Device-Country"] || "";
|
||||||
|
|
||||||
@@ -170,13 +185,13 @@ function AppBase(props) {
|
|||||||
if (!oldDb8Deleted) {
|
if (!oldDb8Deleted) {
|
||||||
dispatch(deleteOldDb8Datas());
|
dispatch(deleteOldDb8Datas());
|
||||||
}
|
}
|
||||||
}, [oldDb8Deleted]);
|
}, [oldDb8Deleted, dispatch]);
|
||||||
|
|
||||||
const hideCursor = useRef(
|
const hideCursor = useRef(
|
||||||
new Job((func) => {
|
new Job((func) => {
|
||||||
func();
|
func();
|
||||||
console.log("hide cursor");
|
console.log("hide cursor");
|
||||||
}, 5000)
|
}, 5000),
|
||||||
);
|
);
|
||||||
|
|
||||||
// 컴포넌트에서 모니터링 시작 - 한시적 모니터링
|
// 컴포넌트에서 모니터링 시작 - 한시적 모니터링
|
||||||
@@ -199,11 +214,13 @@ function AppBase(props) {
|
|||||||
// called by [receive httpHeader, launch, relaunch]
|
// called by [receive httpHeader, launch, relaunch]
|
||||||
const initService = useCallback(
|
const initService = useCallback(
|
||||||
(haveyInit = true) => {
|
(haveyInit = true) => {
|
||||||
|
/*
|
||||||
console.log(
|
console.log(
|
||||||
"<<<<<<<<<<<<< appinfo >>>>>>>>>>>>{heavyInit, appinfo} ",
|
"<<<<<<<<<<<<< appinfo >>>>>>>>>>>>{heavyInit, appinfo} ",
|
||||||
haveyInit,
|
haveyInit,
|
||||||
appinfo
|
appinfo
|
||||||
);
|
);
|
||||||
|
*/
|
||||||
if (httpHeaderRef.current) {
|
if (httpHeaderRef.current) {
|
||||||
if (haveyInit) {
|
if (haveyInit) {
|
||||||
dispatch(changeAppStatus({ connectionFailed: false }));
|
dispatch(changeAppStatus({ connectionFailed: false }));
|
||||||
@@ -211,7 +228,7 @@ function AppBase(props) {
|
|||||||
dispatch(
|
dispatch(
|
||||||
changeAppStatus({
|
changeAppStatus({
|
||||||
cursorVisible: window.PalmSystem?.cursor?.visibility,
|
cursorVisible: window.PalmSystem?.cursor?.visibility,
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
dispatch(getHomeMenu());
|
dispatch(getHomeMenu());
|
||||||
@@ -224,7 +241,7 @@ function AppBase(props) {
|
|||||||
console.log(
|
console.log(
|
||||||
"initService...{haveyInit, launchParams}",
|
"initService...{haveyInit, launchParams}",
|
||||||
haveyInit,
|
haveyInit,
|
||||||
JSON.stringify(launchParams)
|
JSON.stringify(launchParams),
|
||||||
);
|
);
|
||||||
|
|
||||||
// pyh TODO: edit or delete later (line 196 ~ 198)
|
// pyh TODO: edit or delete later (line 196 ~ 198)
|
||||||
@@ -239,7 +256,7 @@ function AppBase(props) {
|
|||||||
contextName: Config.LOG_CONTEXT_NAME.ENTRY,
|
contextName: Config.LOG_CONTEXT_NAME.ENTRY,
|
||||||
messageId: Config.LOG_MESSAGE_ID.ENTRY_INFO,
|
messageId: Config.LOG_MESSAGE_ID.ENTRY_INFO,
|
||||||
entry_menu: "App",
|
entry_menu: "App",
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -249,12 +266,12 @@ function AppBase(props) {
|
|||||||
contextName: Config.LOG_CONTEXT_NAME.SHOPTIME,
|
contextName: Config.LOG_CONTEXT_NAME.SHOPTIME,
|
||||||
messageId: Config.LOG_MESSAGE_ID.VIEW_CHANGE,
|
messageId: Config.LOG_MESSAGE_ID.VIEW_CHANGE,
|
||||||
visible: true,
|
visible: true,
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
clearLaunchParams();
|
clearLaunchParams();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[dispatch]
|
[dispatch],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleRelaunchEvent = useCallback(() => {
|
const handleRelaunchEvent = useCallback(() => {
|
||||||
@@ -265,7 +282,7 @@ function AppBase(props) {
|
|||||||
if (typeof window === "object" && window.PalmSystem) {
|
if (typeof window === "object" && window.PalmSystem) {
|
||||||
window.PalmSystem.activate();
|
window.PalmSystem.activate();
|
||||||
}
|
}
|
||||||
}, [initService, dispatch]);
|
}, [initService, introTermsAgreeRef]);
|
||||||
|
|
||||||
const visibilityChanged = useCallback(() => {
|
const visibilityChanged = useCallback(() => {
|
||||||
console.log("document is hidden", document.hidden);
|
console.log("document is hidden", document.hidden);
|
||||||
@@ -279,7 +296,7 @@ function AppBase(props) {
|
|||||||
foreGroundChangeTimer = setTimeout(() => {
|
foreGroundChangeTimer = setTimeout(() => {
|
||||||
console.log(
|
console.log(
|
||||||
"visibility changed !!! ==> set to foreground cursorVisible",
|
"visibility changed !!! ==> set to foreground cursorVisible",
|
||||||
JSON.stringify(window.PalmSystem?.cursor?.visibility)
|
JSON.stringify(window.PalmSystem?.cursor?.visibility),
|
||||||
); // eslint-disable-line no-console
|
); // eslint-disable-line no-console
|
||||||
if (platform.platformName !== "webos") {
|
if (platform.platformName !== "webos") {
|
||||||
//for debug
|
//for debug
|
||||||
@@ -287,19 +304,19 @@ function AppBase(props) {
|
|||||||
changeAppStatus({
|
changeAppStatus({
|
||||||
isAppForeground: true,
|
isAppForeground: true,
|
||||||
cursorVisible: !platform.touchscreen,
|
cursorVisible: !platform.touchscreen,
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
} else if (typeof window === "object") {
|
} else if (typeof window === "object") {
|
||||||
dispatch(
|
dispatch(
|
||||||
changeAppStatus({
|
changeAppStatus({
|
||||||
isAppForeground: true,
|
isAppForeground: true,
|
||||||
cursorVisible: window.PalmSystem?.cursor?.visibility,
|
cursorVisible: window.PalmSystem?.cursor?.visibility,
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
}, [dispatch, initService]);
|
}, [dispatch]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const keyDownEvent = (event) => {
|
const keyDownEvent = (event) => {
|
||||||
@@ -342,7 +359,7 @@ function AppBase(props) {
|
|||||||
document.removeEventListener("wheel", mouseMoveEvent);
|
document.removeEventListener("wheel", mouseMoveEvent);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}, [dispatch]);
|
}, [dispatch, visibilityChanged, handleRelaunchEvent]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let userDataChanged = false;
|
let userDataChanged = false;
|
||||||
@@ -358,7 +375,7 @@ function AppBase(props) {
|
|||||||
dispatch(
|
dispatch(
|
||||||
changeAppStatus({
|
changeAppStatus({
|
||||||
showLoadingPanel: { show: true, type: "launching" },
|
showLoadingPanel: { show: true, type: "launching" },
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
dispatch(checkFirstLaunch());
|
dispatch(checkFirstLaunch());
|
||||||
@@ -367,13 +384,13 @@ function AppBase(props) {
|
|||||||
getHomeTerms({
|
getHomeTerms({
|
||||||
mbrNo: loginUserData.userNumber,
|
mbrNo: loginUserData.userNumber,
|
||||||
trmsTpCdList: "MST00401, MST00402, MST00405", // 선택약관 추가 25.06
|
trmsTpCdList: "MST00401, MST00402, MST00405", // 선택약관 추가 25.06
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
httpHeaderRef.current = httpHeader;
|
httpHeaderRef.current = httpHeader;
|
||||||
}
|
}
|
||||||
loginUserDataRef.current = loginUserData;
|
loginUserDataRef.current = loginUserData;
|
||||||
}, [httpHeader, deviceId]);
|
}, [httpHeader, deviceId, dispatch, loginUserData]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (
|
if (
|
||||||
@@ -385,8 +402,7 @@ function AppBase(props) {
|
|||||||
) {
|
) {
|
||||||
dispatch(getShoptimeTerms());
|
dispatch(getShoptimeTerms());
|
||||||
}
|
}
|
||||||
}, [webOSVersion, deviceId]);
|
}, [webOSVersion, deviceId, dispatch, deviceCountryCode]);
|
||||||
|
|
||||||
|
|
||||||
// 테스트용 인트로 화면 표시
|
// 테스트용 인트로 화면 표시
|
||||||
// useEffect(() => {
|
// useEffect(() => {
|
||||||
@@ -404,29 +420,15 @@ function AppBase(props) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (introTermsAgree) {
|
if (introTermsAgree) {
|
||||||
// 필수 약관에 동의한 경우
|
initService(true);
|
||||||
// if (shouldShowOptionalTermsPopup) {
|
|
||||||
// 선택 약관 팝업을 띄워야 하는 경우
|
|
||||||
// 3초 후에 팝업을 띄우도록 설정
|
|
||||||
// console.log("App.js optionalTermsTest 팝업 표시");
|
|
||||||
// const timer = setTimeout(() => {
|
|
||||||
// dispatch(setShowPopup({ activePopup: "optionalTermsConfirm" }));
|
|
||||||
// }, 3000); // 3000 milliseconds = 3 seconds
|
|
||||||
|
|
||||||
// 컴포넌트 언마운트 시 타이머 클리어
|
|
||||||
// return () => clearTimeout(timer);
|
|
||||||
// } else {
|
|
||||||
// 선택 약관 팝업이 필요 없는 경우, 바로 서비스 초기화
|
|
||||||
initService(true);
|
|
||||||
// }
|
|
||||||
} else {
|
} else {
|
||||||
// 필수 약관에 동의하지 않은 경우
|
// 필수 약관에 동의하지 않은 경우
|
||||||
dispatch(
|
dispatch(
|
||||||
pushPanel({ name: Config.panel_names.INTRO_PANEL, panelInfo: {} })
|
pushPanel({ name: Config.panel_names.INTRO_PANEL, panelInfo: {} }),
|
||||||
);
|
);
|
||||||
dispatch(changeAppStatus({ showLoadingPanel: { show: false } }));
|
dispatch(changeAppStatus({ showLoadingPanel: { show: false } }));
|
||||||
}
|
}
|
||||||
}, [introTermsAgree, dispatch, initService]);
|
}, [introTermsAgree, dispatch, initService, termsLoading]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const launchParmas = getLaunchParams();
|
const launchParmas = getLaunchParams();
|
||||||
@@ -442,7 +444,7 @@ function AppBase(props) {
|
|||||||
setDeepLink({
|
setDeepLink({
|
||||||
contentTarget: launchParmas.contentTarget,
|
contentTarget: launchParmas.contentTarget,
|
||||||
isDeepLink: true,
|
isDeepLink: true,
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -452,9 +454,9 @@ function AppBase(props) {
|
|||||||
deeplinkId: launchParmas.contentTarget ?? "",
|
deeplinkId: launchParmas.contentTarget ?? "",
|
||||||
linkTpCd,
|
linkTpCd,
|
||||||
logTpNo: Config.LOG_TP_NO.SECOND_LAYER,
|
logTpNo: Config.LOG_TP_NO.SECOND_LAYER,
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
}, [dispatch]);
|
}, [dispatch, initService]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ErrorBoundary>
|
<ErrorBoundary>
|
||||||
|
|||||||
@@ -194,6 +194,17 @@ export const types = {
|
|||||||
GET_SUBTITLE: "GET_SUBTITLE",
|
GET_SUBTITLE: "GET_SUBTITLE",
|
||||||
CLEAR_PLAYER_INFO: "CLEAR_PLAYER_INFO",
|
CLEAR_PLAYER_INFO: "CLEAR_PLAYER_INFO",
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 홈 화면 배너의 비디오 재생 제어를 위한 액션 타입.
|
||||||
|
* 여러 컴포넌트가 동시에 비디오를 재생하려고 할 때 충돌을 방지하고,
|
||||||
|
* 하나의 비디오만 재생되도록 보장하는 역할을 합니다.
|
||||||
|
*
|
||||||
|
* SET_PLAYER_CONTROL: 특정 컴포넌트에게 비디오 재생 제어권을 부여합니다.
|
||||||
|
* CLEAR_PLAYER_CONTROL: 컴포넌트로부터 비디오 재생 제어권을 회수합니다.
|
||||||
|
*/
|
||||||
|
SET_PLAYER_CONTROL: "SET_PLAYER_CONTROL",
|
||||||
|
CLEAR_PLAYER_CONTROL: "CLEAR_PLAYER_CONTROL",
|
||||||
|
|
||||||
// reset action
|
// reset action
|
||||||
RESET_REDUX_STATE: "RESET_REDUX_STATE",
|
RESET_REDUX_STATE: "RESET_REDUX_STATE",
|
||||||
|
|
||||||
|
|||||||
@@ -25,18 +25,21 @@ let startVideoTimer = null;
|
|||||||
//start Full -> modal mode
|
//start Full -> modal mode
|
||||||
let startVideoFocusTimer = null;
|
let startVideoFocusTimer = null;
|
||||||
export const startVideoPlayer =
|
export const startVideoPlayer =
|
||||||
({ modal, modalContainerId, modalClassName, spotlightDisable, ...rest }) =>
|
({ modal, modalContainerId, modalClassName, spotlightDisable, useNewPlayer, ...rest }) =>
|
||||||
(dispatch, getState) => {
|
(dispatch, getState) => {
|
||||||
const panels = getState().panels.panels;
|
const panels = getState().panels.panels;
|
||||||
const topPanel = panels[panels.length - 1];
|
const topPanel = panels[panels.length - 1];
|
||||||
let panelWorkingAction = pushPanel;
|
let panelWorkingAction = pushPanel;
|
||||||
if (topPanel && topPanel.name === panel_names.PLAYER_PANEL) {
|
|
||||||
|
const panelName = useNewPlayer ? panel_names.PLAYER_PANEL_NEW : panel_names.PLAYER_PANEL;
|
||||||
|
|
||||||
|
if (topPanel && topPanel.name === panelName) {
|
||||||
panelWorkingAction = updatePanel;
|
panelWorkingAction = updatePanel;
|
||||||
}
|
}
|
||||||
dispatch(
|
dispatch(
|
||||||
panelWorkingAction(
|
panelWorkingAction(
|
||||||
{
|
{
|
||||||
name: panel_names.PLAYER_PANEL,
|
name: panelName,
|
||||||
panelInfo: {
|
panelInfo: {
|
||||||
modal,
|
modal,
|
||||||
modalContainerId,
|
modalContainerId,
|
||||||
@@ -116,3 +119,52 @@ export const getSubTitle =
|
|||||||
export const CLEAR_PLAYER_INFO = () => ({
|
export const CLEAR_PLAYER_INFO = () => ({
|
||||||
type: types.CLEAR_PLAYER_INFO,
|
type: types.CLEAR_PLAYER_INFO,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 비디오 재생 제어권을 요청하는 액션.
|
||||||
|
* 컴포넌트가 비디오를 재생하고 싶을 때 이 액션을 호출합니다.
|
||||||
|
* 'playerControl' 상태를 확인하여 현재 다른 컴포넌트가 비디오를 제어하고 있지 않은 경우에만
|
||||||
|
* 비디오 플레이어를 활성화(PlayerPanel을 modal로 push)합니다.
|
||||||
|
*
|
||||||
|
* @param {string} ownerId - 비디오 제어권을 요청하는 컴포넌트의 고유 ID.
|
||||||
|
* @param {object} videoInfo - 'startVideoPlayer'에 필요한 비디오 정보.
|
||||||
|
*/
|
||||||
|
export const requestPlayControl =
|
||||||
|
(ownerId, videoInfo) => (dispatch, getState) => {
|
||||||
|
const { playerControl } = getState().home;
|
||||||
|
const currentOwnerId = playerControl.ownerId;
|
||||||
|
|
||||||
|
// 이미 다른 컴포넌트가 제어권을 가지고 있다면, 먼저 해제한다. (선점)
|
||||||
|
if (currentOwnerId && currentOwnerId !== ownerId) {
|
||||||
|
dispatch(releasePlayControl(currentOwnerId, true)); // fromPreemption = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 새로운 제어권을 설정하고 비디오를 재생한다.
|
||||||
|
dispatch({
|
||||||
|
type: types.SET_PLAYER_CONTROL,
|
||||||
|
payload: { ownerId },
|
||||||
|
});
|
||||||
|
dispatch(startVideoPlayer({ ...videoInfo, modal: true }));
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 비디오 재생 제어권을 해제하는 액션.
|
||||||
|
* 컴포넌트가 비디오 재생을 중단할 때(예: 포커스 잃음, 언마운트) 호출합니다.
|
||||||
|
* 현재 제어권을 가진 컴포넌트가 자신일 경우에만 'playerControl' 상태를 초기화하고
|
||||||
|
* 비디오 플레이어를 종료(PlayerPanel을 pop)합니다.
|
||||||
|
*
|
||||||
|
* @param {string} ownerId - 비디오 제어권을 해제하려는 컴포넌트의 고유 ID.
|
||||||
|
* @param {boolean} fromPreemption - 다른 요청에 의해 강제로 해제되었는지 여부.
|
||||||
|
*/
|
||||||
|
export const releasePlayControl = (ownerId, fromPreemption = false) => (dispatch, getState) => {
|
||||||
|
const { playerControl } = getState().home;
|
||||||
|
|
||||||
|
// 제어권을 가진 컴포넌트가 자신일 경우에만 해제
|
||||||
|
// 단, 선점 로직에 의해 호출된 경우는 소유권 확인 없이 즉시 실행
|
||||||
|
if (fromPreemption || playerControl.ownerId === ownerId) {
|
||||||
|
dispatch({
|
||||||
|
type: types.CLEAR_PLAYER_CONTROL,
|
||||||
|
});
|
||||||
|
dispatch(finishVideoPreview());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|||||||
@@ -145,7 +145,7 @@
|
|||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
.flex();
|
.flex();
|
||||||
box-shadow: 0 5px 5px #003 0 6px 7px #0000001a;
|
box-shadow: 0 5px 5px #003, 0 6px 7px #0000001a;
|
||||||
line-height: normal;
|
line-height: normal;
|
||||||
|
|
||||||
&:focus {
|
&:focus {
|
||||||
@@ -165,7 +165,7 @@
|
|||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
.flex();
|
.flex();
|
||||||
box-shadow: 0 5px 5px #003 0 6px 7px #0000001a;
|
box-shadow: 0 5px 5px #003, 0 6px 7px #0000001a;
|
||||||
line-height: normal;
|
line-height: normal;
|
||||||
|
|
||||||
&:focus {
|
&:focus {
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ export default memo(function TItemCard({
|
|||||||
nowProductId,
|
nowProductId,
|
||||||
nowCategory,
|
nowCategory,
|
||||||
nowProductTitle,
|
nowProductTitle,
|
||||||
|
contentId,
|
||||||
...rest
|
...rest
|
||||||
}) {
|
}) {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
|||||||
@@ -286,6 +286,8 @@ export default function TNewPopUp({
|
|||||||
onOptionalAgreeClick, // Agree 버튼용
|
onOptionalAgreeClick, // Agree 버튼용
|
||||||
onOptionalDeclineClick, // Not Now 버튼용
|
onOptionalDeclineClick, // Not Now 버튼용
|
||||||
onIntroTermsAgreeClick, // introTerms Agree 버튼용
|
onIntroTermsAgreeClick, // introTerms Agree 버튼용
|
||||||
|
showAgreeButton = false,
|
||||||
|
onAgreeClick, // onIntroTermsAgreeClick을 대체할 새로운 prop
|
||||||
...rest
|
...rest
|
||||||
}) {
|
}) {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
@@ -750,20 +752,21 @@ export default function TNewPopUp({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ButtonContainerComp className={getClassName(kind, "buttonContainer")}>
|
<ButtonContainerComp className={getClassName(kind, "buttonContainer")}>
|
||||||
{/* 기획 변경으로 Agree 버튼 임시 숨김 처리
|
{showAgreeButton && (
|
||||||
<TButton
|
<TButton
|
||||||
className={getClassName(kind, "agreeButton")}
|
className={getClassName(kind, "agreeButton")}
|
||||||
onClick={onIntroTermsAgreeClick}
|
onClick={onAgreeClick}
|
||||||
spotlightId="figma-terms-agree"
|
spotlightId="figma-terms-agree"
|
||||||
type="agree"
|
type="popup"
|
||||||
>
|
>
|
||||||
{$L('Agree')}
|
{$L('Agree')}
|
||||||
</TButton>
|
</TButton>
|
||||||
*/}
|
)}
|
||||||
<TButton
|
<TButton
|
||||||
className={getClassName(kind, "closeButton")}
|
className={getClassName(kind, "closeButton")}
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
spotlightId="figma-terms-close"
|
spotlightId="figma-terms-close"
|
||||||
|
type="popup"
|
||||||
>
|
>
|
||||||
{$L('Close')}
|
{$L('Close')}
|
||||||
</TButton>
|
</TButton>
|
||||||
|
|||||||
@@ -1082,22 +1082,18 @@
|
|||||||
.figmaTermsButtonContainer {
|
.figmaTermsButtonContainer {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
gap: 12px;
|
align-items: center; // 버튼 수직 정렬을 위해 추가
|
||||||
|
gap: 15px; // 버튼 사이 간격
|
||||||
}
|
}
|
||||||
|
|
||||||
.figmaTermsAgreeButton {
|
.figmaTermsAgreeButton {
|
||||||
.size(240px, 80px);
|
// 이제 TButton의 type="popup" 스타일을 사용하므로,
|
||||||
|
// 여기서는 추가적인 스타일이 필요 없습니다.
|
||||||
// type="agree"의 포커스 시 font-size 변경을 막음
|
// margin-right는 gap으로 대체되었습니다.
|
||||||
&.focused,
|
|
||||||
&:focus { // :focus도 함께 처리
|
|
||||||
font-size: 30px !important;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.figmaTermsCloseButton {
|
.figmaTermsCloseButton {
|
||||||
// TButton의 기본 스타일을 그대로 사용하도록 크기만 지정
|
// TButton의 type="popup" 스타일을 사용합니다.
|
||||||
.size(240px, 80px);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
113
com.twin.app.shoptime/src/hooks/useRightPanelContent.js
Normal file
113
com.twin.app.shoptime/src/hooks/useRightPanelContent.js
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
import React, { useState, useEffect, useRef } from "react";
|
||||||
|
import { $L } from "../utils/helperMethods";
|
||||||
|
import OptionalTermsInfo from "../views/MyPagePanel/MyPageSub/TermsOfService/OptionalTermsInfo";
|
||||||
|
import css from "../views/IntroPanel/IntroPanel.new.module.less";
|
||||||
|
|
||||||
|
const getPanelContent = (
|
||||||
|
focusedItem,
|
||||||
|
termsChecked,
|
||||||
|
privacyChecked,
|
||||||
|
shouldShowBenefitsView
|
||||||
|
) => {
|
||||||
|
const requiredItemIds = [
|
||||||
|
"termsCheckbox",
|
||||||
|
"termsButton",
|
||||||
|
"privacyCheckbox",
|
||||||
|
"privacyButton",
|
||||||
|
];
|
||||||
|
const isRequiredFocused = requiredItemIds.includes(focusedItem);
|
||||||
|
|
||||||
|
// 1순위: 포커스가 필수 약관 관련 아이템에 있는 경우
|
||||||
|
if (isRequiredFocused) {
|
||||||
|
// 필수 약관이 동의되지 않았을 때 (경고 메시지 포함)
|
||||||
|
if (!termsChecked || !privacyChecked) {
|
||||||
|
return (
|
||||||
|
<div className={css.requiredInfoPanel}>
|
||||||
|
<p className={css.infoText}>{$L("Required Consent")}</p>
|
||||||
|
<p className={css.infoText}>
|
||||||
|
{$L("(Necessary for using the service)")}
|
||||||
|
</p>
|
||||||
|
<div className={css.warningContainer}>
|
||||||
|
<p className={css.warningText}>
|
||||||
|
{$L("Please agree to the required Terms & Conditions and")}
|
||||||
|
</p>
|
||||||
|
<p className={css.warningText}>
|
||||||
|
{$L("Privacy Policy to start enjoying Shop Time")}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// 필수 약관이 모두 동의되었을 때 (단순 안내 메시지)
|
||||||
|
return (
|
||||||
|
<div className={css.requiredInfoPanel}>
|
||||||
|
<p className={css.infoText}>{$L("Required Consent")}</p>
|
||||||
|
<p className={css.infoText}>
|
||||||
|
{$L("(Necessary for using the service)")}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2순위: 포커스가 그 외 모든 아이템에 있는 경우
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{shouldShowBenefitsView ? (
|
||||||
|
<div className={css.optionalDescription}>
|
||||||
|
{$L(
|
||||||
|
'By checking "Optional terms", you allow Shop Time to use your activity (views, purchases, searches, etc.) to show you more relevant content, product recommendations, special offers, and ads. If you do not check, you can still use all basic Shop Time features'
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<OptionalTermsInfo
|
||||||
|
displayMode="image"
|
||||||
|
imageTitle={$L("Agree and Enjoy Special Benefits")}
|
||||||
|
spotlightId="optional-terms-info"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const useRightPanelContent = (
|
||||||
|
focusedItem,
|
||||||
|
termsChecked,
|
||||||
|
privacyChecked,
|
||||||
|
shouldShowBenefitsView
|
||||||
|
) => {
|
||||||
|
const [content, setContent] = useState(() =>
|
||||||
|
getPanelContent(
|
||||||
|
focusedItem,
|
||||||
|
termsChecked,
|
||||||
|
privacyChecked,
|
||||||
|
shouldShowBenefitsView
|
||||||
|
)
|
||||||
|
);
|
||||||
|
const updateTimeoutRef = useRef(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (updateTimeoutRef.current) {
|
||||||
|
clearTimeout(updateTimeoutRef.current);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateTimeoutRef.current = setTimeout(() => {
|
||||||
|
const newContent = getPanelContent(
|
||||||
|
focusedItem,
|
||||||
|
termsChecked,
|
||||||
|
privacyChecked,
|
||||||
|
shouldShowBenefitsView
|
||||||
|
);
|
||||||
|
setContent(newContent);
|
||||||
|
}, 50);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
if (updateTimeoutRef.current) {
|
||||||
|
clearTimeout(updateTimeoutRef.current);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, [focusedItem, termsChecked, privacyChecked, shouldShowBenefitsView]);
|
||||||
|
|
||||||
|
return content;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useRightPanelContent;
|
||||||
40
com.twin.app.shoptime/src/hooks/useSafeFocusState.js
Normal file
40
com.twin.app.shoptime/src/hooks/useSafeFocusState.js
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import { useState, useCallback, useRef } from "react";
|
||||||
|
|
||||||
|
const useSafeFocusState = () => {
|
||||||
|
const [focusedItem, setFocusedItem] = useState(null);
|
||||||
|
const blurTimeoutRef = useRef(null);
|
||||||
|
|
||||||
|
const setFocusAsync = useCallback((item) => {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
if (blurTimeoutRef.current) {
|
||||||
|
clearTimeout(blurTimeoutRef.current);
|
||||||
|
blurTimeoutRef.current = null;
|
||||||
|
}
|
||||||
|
if (process.env.NODE_ENV === "development") {
|
||||||
|
console.log("[useSafeFocusState] Focus Set:", item);
|
||||||
|
}
|
||||||
|
setFocusedItem(item);
|
||||||
|
resolve(item);
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const clearFocusAsync = useCallback((delay = 0) => {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
if (blurTimeoutRef.current) {
|
||||||
|
clearTimeout(blurTimeoutRef.current);
|
||||||
|
}
|
||||||
|
blurTimeoutRef.current = setTimeout(() => {
|
||||||
|
if (process.env.NODE_ENV === "development") {
|
||||||
|
console.log("[useSafeFocusState] Focus Cleared");
|
||||||
|
}
|
||||||
|
setFocusedItem(null);
|
||||||
|
blurTimeoutRef.current = null;
|
||||||
|
resolve();
|
||||||
|
}, delay);
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return { focusedItem, setFocusAsync, clearFocusAsync };
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useSafeFocusState;
|
||||||
95
com.twin.app.shoptime/src/hooks/useTermsStateMachine.js
Normal file
95
com.twin.app.shoptime/src/hooks/useTermsStateMachine.js
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
import { useState, useCallback } from "react";
|
||||||
|
|
||||||
|
const useTermsStateMachine = (initialState) => {
|
||||||
|
const [isTransitioning, setIsTransitioning] = useState(false);
|
||||||
|
const [state, setState] = useState({
|
||||||
|
termsChecked: false,
|
||||||
|
privacyChecked: false,
|
||||||
|
optionalChecked: false,
|
||||||
|
selectAllChecked: false,
|
||||||
|
error: null, // 1. 에러 상태 추가
|
||||||
|
...initialState,
|
||||||
|
});
|
||||||
|
|
||||||
|
const updateStateAsync = useCallback(
|
||||||
|
async (updates) => {
|
||||||
|
if (isTransitioning) {
|
||||||
|
const err = new Error("State transition already in progress.");
|
||||||
|
if (process.env.NODE_ENV === "development") {
|
||||||
|
console.warn("[useTermsStateMachine] Rejected:", err.message);
|
||||||
|
}
|
||||||
|
// 2. 에러 상태를 설정하고 throw
|
||||||
|
setState((s) => ({ ...s, error: err }));
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsTransitioning(true);
|
||||||
|
// 3. 이전 에러 상태 초기화
|
||||||
|
setState((s) => ({ ...s, error: null }));
|
||||||
|
|
||||||
|
try {
|
||||||
|
const newState = await new Promise((resolve) => {
|
||||||
|
setState((currentState) => {
|
||||||
|
const nextState = { ...currentState, ...updates };
|
||||||
|
|
||||||
|
const {
|
||||||
|
termsChecked: currentTerms,
|
||||||
|
privacyChecked: currentPrivacy,
|
||||||
|
optionalChecked: currentOptional,
|
||||||
|
} = currentState;
|
||||||
|
|
||||||
|
const termsChecked =
|
||||||
|
"termsChecked" in updates
|
||||||
|
? updates.termsChecked
|
||||||
|
: currentTerms;
|
||||||
|
const privacyChecked =
|
||||||
|
"privacyChecked" in updates
|
||||||
|
? updates.privacyChecked
|
||||||
|
: currentPrivacy;
|
||||||
|
const optionalChecked =
|
||||||
|
"optionalChecked" in updates
|
||||||
|
? updates.optionalChecked
|
||||||
|
: currentOptional;
|
||||||
|
|
||||||
|
if (
|
||||||
|
"termsChecked" in updates ||
|
||||||
|
"privacyChecked" in updates ||
|
||||||
|
"optionalChecked" in updates
|
||||||
|
) {
|
||||||
|
nextState.selectAllChecked =
|
||||||
|
termsChecked && privacyChecked && optionalChecked;
|
||||||
|
} else if ("selectAllChecked" in updates) {
|
||||||
|
nextState.termsChecked = updates.selectAllChecked;
|
||||||
|
nextState.privacyChecked = updates.selectAllChecked;
|
||||||
|
nextState.optionalChecked = updates.selectAllChecked;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process.env.NODE_ENV === "development") {
|
||||||
|
console.log(
|
||||||
|
"[useTermsStateMachine] State transition:",
|
||||||
|
currentState,
|
||||||
|
"->",
|
||||||
|
nextState
|
||||||
|
);
|
||||||
|
}
|
||||||
|
resolve(nextState);
|
||||||
|
return nextState;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
setIsTransitioning(false);
|
||||||
|
return newState;
|
||||||
|
} catch (error) {
|
||||||
|
setIsTransitioning(false);
|
||||||
|
console.error("[useTermsStateMachine] State update failed:", error);
|
||||||
|
// 4. 에러 상태를 설정하고 다시 throw
|
||||||
|
setState((s) => ({ ...s, error }));
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[isTransitioning]
|
||||||
|
);
|
||||||
|
|
||||||
|
return { state, updateStateAsync };
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useTermsStateMachine;
|
||||||
@@ -21,6 +21,9 @@ const initialState = {
|
|||||||
homeInfo: null,
|
homeInfo: null,
|
||||||
curationId: "",
|
curationId: "",
|
||||||
curationTitle: "",
|
curationTitle: "",
|
||||||
|
playerControl: {
|
||||||
|
ownerId: null,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const homeReducer = (state = initialState, action) => {
|
export const homeReducer = (state = initialState, action) => {
|
||||||
@@ -189,6 +192,26 @@ export const homeReducer = (state = initialState, action) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case types.SET_PLAYER_CONTROL: {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
playerControl: {
|
||||||
|
...state.playerControl,
|
||||||
|
ownerId: action.payload.ownerId,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
case types.CLEAR_PLAYER_CONTROL: {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
playerControl: {
|
||||||
|
...state.playerControl,
|
||||||
|
ownerId: null,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ export const panel_names = {
|
|||||||
CONFIRM_PANEL: "confirmpanel",
|
CONFIRM_PANEL: "confirmpanel",
|
||||||
DETAIL_PANEL: "detailpanel",
|
DETAIL_PANEL: "detailpanel",
|
||||||
PLAYER_PANEL: "playerpanel",
|
PLAYER_PANEL: "playerpanel",
|
||||||
|
PLAYER_PANEL_NEW: "playerpanel.new",
|
||||||
CHECKOUT_PANEL: "checkoutpanel",
|
CHECKOUT_PANEL: "checkoutpanel",
|
||||||
THEME_CURATION_PANEL: "themeCurationPanel",
|
THEME_CURATION_PANEL: "themeCurationPanel",
|
||||||
IMAGE_PANEL: "imagepanel",
|
IMAGE_PANEL: "imagepanel",
|
||||||
|
|||||||
@@ -6,28 +6,38 @@ import { useDispatch, useSelector } from "react-redux";
|
|||||||
import Spotlight from "@enact/spotlight";
|
import Spotlight from "@enact/spotlight";
|
||||||
import { SpotlightContainerDecorator } from "@enact/spotlight/SpotlightContainerDecorator";
|
import { SpotlightContainerDecorator } from "@enact/spotlight/SpotlightContainerDecorator";
|
||||||
import Spottable from "@enact/spotlight/Spottable";
|
import Spottable from "@enact/spotlight/Spottable";
|
||||||
import { $L, scaleH, scaleW } from '../../../utils/helperMethods';
|
import { $L, scaleH, scaleW } from "../../../utils/helperMethods";
|
||||||
import { setDefaultFocus, setShowPopup, fetchCurrentUserHomeTerms } from "../../../actions/homeActions";
|
import {
|
||||||
|
setDefaultFocus,
|
||||||
|
setShowPopup,
|
||||||
|
fetchCurrentUserHomeTerms,
|
||||||
|
} from "../../../actions/homeActions";
|
||||||
import { changeAppStatus } from "../../../actions/commonActions";
|
import { changeAppStatus } from "../../../actions/commonActions";
|
||||||
import { setMyPageTermsAgree } from '../../../actions/myPageActions';
|
import { setMyPageTermsAgree } from "../../../actions/myPageActions";
|
||||||
import { pushPanel } from "../../../actions/panelActions";
|
import { pushPanel } from "../../../actions/panelActions";
|
||||||
|
import {
|
||||||
|
requestPlayControl,
|
||||||
|
releasePlayControl,
|
||||||
|
} from "../../../actions/playActions";
|
||||||
import CustomImage from "../../../components/CustomImage/CustomImage";
|
import CustomImage from "../../../components/CustomImage/CustomImage";
|
||||||
import css from "./HomeBanner.module.less";
|
import css from "./HomeBanner.module.less";
|
||||||
import Random from "./RandomUnit";
|
import Random from "./RandomUnit";
|
||||||
import Rolling from "./RollingUnit";
|
import Rolling from "./RollingUnit";
|
||||||
|
import RandomUnitNew from "./RandomUnit.new";
|
||||||
import TNewPopUp from "../../../components/TPopUp/TNewPopUp";
|
import TNewPopUp from "../../../components/TPopUp/TNewPopUp";
|
||||||
import TButtonScroller from "../../../components/TButtonScroller/TButtonScroller";
|
// import TButtonScroller from "../../../components/TButtonScroller/TButtonScroller";
|
||||||
import OptionalConfirm from "../../../components/Optional/OptionalConfirm";
|
import OptionalConfirm from "../../../components/Optional/OptionalConfirm";
|
||||||
import * as Config from "../../../utils/Config";
|
// import * as Config from "../../../utils/Config";
|
||||||
|
import PersistentVideoUnit from "./PersistentVideoUnit";
|
||||||
|
|
||||||
const SpottableComponent = Spottable("div");
|
const SpottableComponent = Spottable("div");
|
||||||
const Container = SpotlightContainerDecorator(
|
const Container = SpotlightContainerDecorator(
|
||||||
{ enterTo: "last-focused" },
|
{ enterTo: "last-focused" },
|
||||||
"div"
|
"div",
|
||||||
);
|
);
|
||||||
const ContainerBasic = SpotlightContainerDecorator(
|
const ContainerBasic = SpotlightContainerDecorator(
|
||||||
{ enterTo: "last-focused" },
|
{ enterTo: "last-focused" },
|
||||||
"div"
|
"div",
|
||||||
);
|
);
|
||||||
|
|
||||||
export default function HomeBanner({
|
export default function HomeBanner({
|
||||||
@@ -38,11 +48,11 @@ export default function HomeBanner({
|
|||||||
}) {
|
}) {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const homeTopDisplayInfo = useSelector(
|
const homeTopDisplayInfo = useSelector(
|
||||||
(state) => state.home.homeTopDisplayInfo
|
(state) => state.home.homeTopDisplayInfo,
|
||||||
);
|
);
|
||||||
|
|
||||||
const bannerDataList = useSelector(
|
const bannerDataList = useSelector(
|
||||||
(state) => state.home.bannerData?.bannerInfos
|
(state) => state.home.bannerData?.bannerInfos,
|
||||||
);
|
);
|
||||||
|
|
||||||
const popupVisible = useSelector((state) => state.common.popup.popupVisible);
|
const popupVisible = useSelector((state) => state.common.popup.popupVisible);
|
||||||
@@ -57,9 +67,39 @@ export default function HomeBanner({
|
|||||||
}
|
}
|
||||||
}, [handleItemFocus]);
|
}, [handleItemFocus]);
|
||||||
|
|
||||||
|
const handleSecondBannerFocus = useCallback(() => {
|
||||||
|
const secondBannerData = bannerDataList?.[1];
|
||||||
|
if (secondBannerData) {
|
||||||
|
const randomData =
|
||||||
|
secondBannerData.bannerDetailInfos[secondBannerData.randomIndex];
|
||||||
|
const videoInfo = {
|
||||||
|
showUrl: randomData.showUrl,
|
||||||
|
patnrId: randomData.patnrId,
|
||||||
|
showId: randomData.showId,
|
||||||
|
shptmBanrTpNm: randomData.showId ? randomData.shptmBanrTpNm : "MEDIA",
|
||||||
|
lgCatCd: randomData.lgCatCd,
|
||||||
|
chanId: randomData.brdcChnlId,
|
||||||
|
modal: true,
|
||||||
|
modalContainerId: "banner1",
|
||||||
|
modalClassName: css.videoModal,
|
||||||
|
isVerticalModal: true, // Assuming second banner is horizontal, so modal is vertical
|
||||||
|
};
|
||||||
|
dispatch(requestPlayControl("banner1_preview", videoInfo));
|
||||||
|
}
|
||||||
|
if (handleItemFocus) {
|
||||||
|
handleItemFocus();
|
||||||
|
}
|
||||||
|
}, [dispatch, bannerDataList, handleItemFocus]);
|
||||||
|
|
||||||
|
const handleSecondBannerBlur = useCallback(() => {
|
||||||
|
dispatch(releasePlayControl("banner1_preview"));
|
||||||
|
}, [dispatch]);
|
||||||
|
|
||||||
const termsData = useSelector((state) => state.home.termsData);
|
const termsData = useSelector((state) => state.home.termsData);
|
||||||
const optionalTermsData = useSelector((state) =>
|
const optionalTermsData = useSelector((state) =>
|
||||||
state.home.termsData?.data?.terms.find(term => term.trmsTpCd === "MST00405")
|
state.home.termsData?.data?.terms.find(
|
||||||
|
(term) => term.trmsTpCd === "MST00405",
|
||||||
|
),
|
||||||
);
|
);
|
||||||
const termsLoading = useSelector((state) => state.common.termsLoading);
|
const termsLoading = useSelector((state) => state.common.termsLoading);
|
||||||
const isGnbOpened = useSelector((state) => state.common.isGnbOpened);
|
const isGnbOpened = useSelector((state) => state.common.isGnbOpened);
|
||||||
@@ -68,7 +108,8 @@ export default function HomeBanner({
|
|||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// 팝업표시 상태
|
// 팝업표시 상태
|
||||||
const [isOptionalConfirmVisible, setIsOptionalConfirmVisible] = useState(false);
|
const [isOptionalConfirmVisible, setIsOptionalConfirmVisible] =
|
||||||
|
useState(false);
|
||||||
const [isOptionalTermsVisible, setIsOptionalTermsVisible] = useState(false);
|
const [isOptionalTermsVisible, setIsOptionalTermsVisible] = useState(false);
|
||||||
|
|
||||||
// 선택약관 팝업 표시 여부
|
// 선택약관 팝업 표시 여부
|
||||||
@@ -80,35 +121,42 @@ export default function HomeBanner({
|
|||||||
if (!terms) {
|
if (!terms) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const optionalTerm = terms.find(term => term.trmsTpCd === "MST00405");
|
const optionalTerm = terms.find((term) => term.trmsTpCd === "MST00405");
|
||||||
return optionalTerm ? optionalTerm.trmsPopFlag === 'Y' && optionalTerm.trmsAgrFlag === 'N' : false;
|
return optionalTerm
|
||||||
|
? optionalTerm.trmsPopFlag === "Y" && optionalTerm.trmsAgrFlag === "N"
|
||||||
|
: false;
|
||||||
}, [termsData, termsLoading, isGnbOpened]);
|
}, [termsData, termsLoading, isGnbOpened]);
|
||||||
|
|
||||||
const handleOptionalAgree = useCallback(() => {
|
const handleOptionalAgree = useCallback(() => {
|
||||||
console.log('handleAgree Click');
|
console.log("handleAgree Click");
|
||||||
|
|
||||||
// 약관 동의할 항목들 (string array)
|
// 약관 동의할 항목들 (string array)
|
||||||
const termsList = ["TID0000222", "TID0000223", "TID0000232"];
|
const termsList = ["TID0000222", "TID0000223", "TID0000232"];
|
||||||
// 동의하지 않을 항목들 (빈 배열)
|
// 동의하지 않을 항목들 (빈 배열)
|
||||||
const notTermsList = [];
|
const notTermsList = [];
|
||||||
console.log('OptionalTermsConfirm -약관 동의 API 호출 파라미터:', { termsList, notTermsList });
|
console.log("OptionalTermsConfirm -약관 동의 API 호출 파라미터:", {
|
||||||
|
termsList,
|
||||||
|
notTermsList,
|
||||||
|
});
|
||||||
const callback = (response) => {
|
const callback = (response) => {
|
||||||
if (response.retCode === "000" || response.retCode === 0) {
|
if (response.retCode === "000" || response.retCode === 0) {
|
||||||
console.log('약관 동의 성공:', response);
|
console.log("약관 동의 성공:", response);
|
||||||
// 약관 정보 갱신
|
// 약관 정보 갱신
|
||||||
dispatch(fetchCurrentUserHomeTerms());
|
dispatch(fetchCurrentUserHomeTerms());
|
||||||
} else {
|
} else {
|
||||||
console.error('약관 동의 실패:', response);
|
console.error("약관 동의 실패:", response);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log('OptionalTermsConfirm - 약관 동의 API 호출 payload:', { termsList, notTermsList });
|
console.log("OptionalTermsConfirm - 약관 동의 API 호출 payload:", {
|
||||||
|
termsList,
|
||||||
|
notTermsList,
|
||||||
|
});
|
||||||
dispatch(setMyPageTermsAgree({ termsList, notTermsList }, callback));
|
dispatch(setMyPageTermsAgree({ termsList, notTermsList }, callback));
|
||||||
|
|
||||||
}, [dispatch]);
|
}, [dispatch]);
|
||||||
|
|
||||||
const handleOptionalTermsClick = useCallback(() => {
|
const handleOptionalTermsClick = useCallback(() => {
|
||||||
console.log('약관 자세히 보기 클릭');
|
console.log("약관 자세히 보기 클릭");
|
||||||
setIsOptionalConfirmVisible(false);
|
setIsOptionalConfirmVisible(false);
|
||||||
setIsOptionalTermsVisible(true);
|
setIsOptionalTermsVisible(true);
|
||||||
// 약관 상세 팝업을 띄우는 로직 추가
|
// 약관 상세 팝업을 띄우는 로직 추가
|
||||||
@@ -120,7 +168,7 @@ export default function HomeBanner({
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleOptionalDeclineClick = useCallback(() => {
|
const handleOptionalDeclineClick = useCallback(() => {
|
||||||
console.log('거절/다음에 하기 버튼 클릭');
|
console.log("거절/다음에 하기 버튼 클릭");
|
||||||
setIsOptionalConfirmVisible(false);
|
setIsOptionalConfirmVisible(false);
|
||||||
// 거절 처리 로직 추가
|
// 거절 처리 로직 추가
|
||||||
}, []);
|
}, []);
|
||||||
@@ -139,7 +187,6 @@ export default function HomeBanner({
|
|||||||
setIsOptionalTermsVisible(false);
|
setIsOptionalTermsVisible(false);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
const _handleShelfFocus = useCallback(() => {
|
const _handleShelfFocus = useCallback(() => {
|
||||||
if (handleShelfFocus) {
|
if (handleShelfFocus) {
|
||||||
@@ -164,7 +211,7 @@ export default function HomeBanner({
|
|||||||
}
|
}
|
||||||
} else if (
|
} else if (
|
||||||
bannerDetailInfos.find(
|
bannerDetailInfos.find(
|
||||||
(el) => el.shptmBanrTpNm === "LIVE" || el.shptmBanrTpNm === "VOD"
|
(el) => el.shptmBanrTpNm === "LIVE" || el.shptmBanrTpNm === "VOD",
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
targetIndex = i;
|
targetIndex = i;
|
||||||
@@ -217,11 +264,29 @@ export default function HomeBanner({
|
|||||||
// 컴포넌트 언마운트 시 타이머 클리어
|
// 컴포넌트 언마운트 시 타이머 클리어
|
||||||
return () => clearTimeout(timer);
|
return () => clearTimeout(timer);
|
||||||
}
|
}
|
||||||
}, [shouldShowOptionalTermsPopup, termsLoading]);
|
}, [shouldShowOptionalTermsPopup, termsLoading]);
|
||||||
|
|
||||||
const renderItem = useCallback(
|
const renderItem = useCallback(
|
||||||
(index, isHorizontal) => {
|
(index, isHorizontal) => {
|
||||||
const data = bannerDataList?.[index] ?? {};
|
const data = bannerDataList?.[index] ?? {};
|
||||||
|
|
||||||
|
if (index === 1) {
|
||||||
|
return (
|
||||||
|
<div className={!isHorizontal ? css.imgBox : undefined}>
|
||||||
|
<RandomUnitNew
|
||||||
|
bannerData={data}
|
||||||
|
isHorizontal={isHorizontal}
|
||||||
|
key={"banner" + index}
|
||||||
|
spotlightId={"banner" + index}
|
||||||
|
handleShelfFocus={_handleShelfFocus}
|
||||||
|
onFocus={handleSecondBannerFocus}
|
||||||
|
onBlur={handleSecondBannerBlur}
|
||||||
|
randomNumber={data.randomIndex}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={!isHorizontal ? css.imgBox : undefined}>
|
<div className={!isHorizontal ? css.imgBox : undefined}>
|
||||||
{data.shptmDspyTpNm === "Rolling" ? (
|
{data.shptmDspyTpNm === "Rolling" ? (
|
||||||
@@ -263,7 +328,60 @@ export default function HomeBanner({
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
[_handleItemFocus, _handleShelfFocus, bannerDataList]
|
[
|
||||||
|
bannerDataList,
|
||||||
|
_handleItemFocus,
|
||||||
|
_handleShelfFocus,
|
||||||
|
handleSecondBannerFocus,
|
||||||
|
handleSecondBannerBlur,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
const renderItemPersistentVideo = useCallback(
|
||||||
|
(index, isHorizontal) => {
|
||||||
|
const data = bannerDataList?.[index] ?? {};
|
||||||
|
return (
|
||||||
|
<div className={!isHorizontal ? css.imgBox : undefined}>
|
||||||
|
{data.shptmDspyTpNm === "Rolling" ? (
|
||||||
|
<Rolling
|
||||||
|
bannerData={data}
|
||||||
|
isHorizontal={isHorizontal}
|
||||||
|
key={"banner" + index}
|
||||||
|
spotlightId={"banner" + index}
|
||||||
|
handleShelfFocus={_handleShelfFocus}
|
||||||
|
handleItemFocus={_handleItemFocus}
|
||||||
|
/>
|
||||||
|
) : data.shptmDspyTpNm === "Random" ? (
|
||||||
|
<PersistentVideoUnit
|
||||||
|
bannerData={data}
|
||||||
|
isHorizontal={isHorizontal}
|
||||||
|
key={"banner" + index}
|
||||||
|
spotlightId={"banner" + index}
|
||||||
|
handleShelfFocus={_handleShelfFocus}
|
||||||
|
handleItemFocus={_handleItemFocus}
|
||||||
|
randomNumber={data.randomIndex}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<SpottableComponent spotlightId={"banner" + index}>
|
||||||
|
<CustomImage
|
||||||
|
delay={0}
|
||||||
|
src={
|
||||||
|
isHorizontal
|
||||||
|
? homeTopDisplayInfo.wdthtpImgPath1
|
||||||
|
: homeTopDisplayInfo.vtctpImgPath1
|
||||||
|
}
|
||||||
|
aria-label={
|
||||||
|
isHorizontal
|
||||||
|
? homeTopDisplayInfo.wdthtpImgNm1
|
||||||
|
: homeTopDisplayInfo.vtctpImgNm1
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</SpottableComponent>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
[_handleItemFocus, _handleShelfFocus, bannerDataList, homeTopDisplayInfo],
|
||||||
);
|
);
|
||||||
|
|
||||||
const renderLayout = useCallback(() => {
|
const renderLayout = useCallback(() => {
|
||||||
@@ -272,6 +390,7 @@ export default function HomeBanner({
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ContainerBasic className={css.smallBox}>
|
<ContainerBasic className={css.smallBox}>
|
||||||
|
{/* {renderItemPersistentVideo(0, true)} */}
|
||||||
{renderItem(0, true)}
|
{renderItem(0, true)}
|
||||||
{renderItem(1, true)}
|
{renderItem(1, true)}
|
||||||
</ContainerBasic>
|
</ContainerBasic>
|
||||||
@@ -306,7 +425,7 @@ export default function HomeBanner({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}, [selectTemplate, renderItem]);
|
}, [selectTemplate, renderItem, renderItemPersistentVideo]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -327,41 +446,23 @@ export default function HomeBanner({
|
|||||||
onOptionalDeclineClick={handleOptionalDeclineClick}
|
onOptionalDeclineClick={handleOptionalDeclineClick}
|
||||||
customPosition={true}
|
customPosition={true}
|
||||||
position={{
|
position={{
|
||||||
position: 'absolute',
|
position: "absolute",
|
||||||
top: '342px', // 가운데를 기준으로 한 좌표 (1080/2) - 198
|
top: "342px", // 가운데를 기준으로 한 좌표 (1080/2) - 198
|
||||||
left: '0px',
|
left: "0px",
|
||||||
bottom: 'unset',
|
bottom: "unset",
|
||||||
transform: 'none',
|
transform: "none",
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{/* 선택약관 자세히 보기 팝업 */}
|
{/* 선택약관 자세히 보기 팝업 */}
|
||||||
<TNewPopUp
|
<TNewPopUp
|
||||||
kind="introTermsPopup"
|
kind="figmaTermsPopup"
|
||||||
open={isOptionalTermsVisible}
|
open={isOptionalTermsVisible}
|
||||||
|
title={$L("Optional Terms")}
|
||||||
|
text={optionalTermsData?.trmsCntt || ""}
|
||||||
onClose={handleTermsPopupClosed}
|
onClose={handleTermsPopupClosed}
|
||||||
onClick={handleTermsPopupClosed}
|
onAgreeClick={handleTermsPopupAgree}
|
||||||
onIntroTermsAgreeClick={handleTermsPopupAgree}
|
showAgreeButton={true}
|
||||||
hasButton
|
/>
|
||||||
button1Text={$L("Close")}
|
|
||||||
>
|
|
||||||
{optionalTermsData && (
|
|
||||||
<div className={css.termsViewerContent}>
|
|
||||||
<div className={css.termsViewerTitle}>{$L("Optional Terms")}</div>
|
|
||||||
<TButtonScroller
|
|
||||||
boxHeight={scaleH(300)}
|
|
||||||
width={scaleW(980)}
|
|
||||||
className={css.termsDescription}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
className={css.termsDesc}
|
|
||||||
dangerouslySetInnerHTML={{
|
|
||||||
__html: optionalTermsData.trmsCntt,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</TButtonScroller>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</TNewPopUp>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,155 @@
|
|||||||
|
import React, { useCallback, useEffect } from "react";
|
||||||
|
import { useDispatch } from "react-redux";
|
||||||
|
import classNames from "classnames";
|
||||||
|
import Spottable from "@enact/spotlight/Spottable";
|
||||||
|
import SpotlightContainerDecorator from "@enact/spotlight/SpotlightContainerDecorator";
|
||||||
|
|
||||||
|
import { requestPlayControl, releasePlayControl, startVideoPlayer } from "../../../actions/playActions";
|
||||||
|
import CustomImage from "../../../components/CustomImage/CustomImage";
|
||||||
|
import liveShow from "../../../../assets/images/tag-liveshow.png";
|
||||||
|
import emptyHorImage from "../../../../assets/images/img-home-banner-empty-hor.png";
|
||||||
|
import emptyVerImage from "../../../../assets/images/img-home-banner-empty-ver.png";
|
||||||
|
import btnPlay from "../../../../assets/images/btn/btn-play-thumb-nor.png";
|
||||||
|
import defaultLogoImg from "../../../../assets/images/ic-tab-partners-default@3x.png";
|
||||||
|
import css from "./RandomUnit.module.less";
|
||||||
|
|
||||||
|
const SpottableComponent = Spottable("div");
|
||||||
|
const Container = SpotlightContainerDecorator(
|
||||||
|
{ enterTo: "last-focused" },
|
||||||
|
"div"
|
||||||
|
);
|
||||||
|
|
||||||
|
const PersistentVideoUnit = (props) => {
|
||||||
|
const { bannerData, spotlightId, isHorizontal, randomNumber, handleItemFocus, handleShelfFocus } = props;
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
const randomData = bannerData?.bannerDetailInfos?.[randomNumber];
|
||||||
|
|
||||||
|
const requestVideo = useCallback(() => {
|
||||||
|
if (randomData) {
|
||||||
|
const videoInfo = {
|
||||||
|
showUrl: randomData.showUrl,
|
||||||
|
patnrId: randomData.patnrId,
|
||||||
|
showId: randomData.showId,
|
||||||
|
shptmBanrTpNm: randomData.showId ? randomData.shptmBanrTpNm : "MEDIA",
|
||||||
|
lgCatCd: randomData.lgCatCd,
|
||||||
|
chanId: randomData.brdcChnlId,
|
||||||
|
modal: true,
|
||||||
|
modalContainerId: spotlightId,
|
||||||
|
modalClassName: css.videoModal,
|
||||||
|
isVerticalModal: !isHorizontal,
|
||||||
|
};
|
||||||
|
dispatch(requestPlayControl(spotlightId, videoInfo));
|
||||||
|
}
|
||||||
|
}, [dispatch, randomData, spotlightId, isHorizontal]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
requestVideo();
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
dispatch(releasePlayControl(spotlightId));
|
||||||
|
};
|
||||||
|
}, [dispatch, requestVideo, spotlightId]);
|
||||||
|
|
||||||
|
const handleFocus = useCallback(() => {
|
||||||
|
requestVideo();
|
||||||
|
if (handleItemFocus) {
|
||||||
|
handleItemFocus();
|
||||||
|
}
|
||||||
|
}, [requestVideo, handleItemFocus]);
|
||||||
|
|
||||||
|
const handleClick = useCallback(() => {
|
||||||
|
if (randomData) {
|
||||||
|
const videoInfo = {
|
||||||
|
showUrl: randomData.showUrl,
|
||||||
|
patnrId: randomData.patnrId,
|
||||||
|
showId: randomData.showId,
|
||||||
|
shptmBanrTpNm: randomData.showId ? randomData.shptmBanrTpNm : "MEDIA",
|
||||||
|
lgCatCd: randomData.lgCatCd,
|
||||||
|
chanId: randomData.brdcChnlId,
|
||||||
|
modal: false,
|
||||||
|
};
|
||||||
|
dispatch(startVideoPlayer(videoInfo));
|
||||||
|
}
|
||||||
|
}, [dispatch, randomData]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container
|
||||||
|
className={classNames(
|
||||||
|
css.rollingWrap,
|
||||||
|
isHorizontal && css.isHorizontalWrap
|
||||||
|
)}
|
||||||
|
onFocus={handleShelfFocus}
|
||||||
|
>
|
||||||
|
<SpottableComponent
|
||||||
|
className={classNames(
|
||||||
|
css.itemBox,
|
||||||
|
isHorizontal && css.isHorizontal
|
||||||
|
)}
|
||||||
|
onFocus={handleFocus}
|
||||||
|
onClick={handleClick}
|
||||||
|
spotlightId={spotlightId}
|
||||||
|
aria-label={
|
||||||
|
randomData?.shptmBanrTpNm === "LIVE"
|
||||||
|
? "LIVE " + randomData?.showNm
|
||||||
|
: randomData?.showNm
|
||||||
|
}
|
||||||
|
alt={"LIVE"}
|
||||||
|
>
|
||||||
|
{randomData?.shptmBanrTpNm === "LIVE" && (
|
||||||
|
<p className={css.liveIcon}>
|
||||||
|
<CustomImage
|
||||||
|
delay={0}
|
||||||
|
src={liveShow}
|
||||||
|
animationSpeed="fast"
|
||||||
|
ariaLabel="LIVE icon"
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div
|
||||||
|
className={classNames(
|
||||||
|
css.itemBox,
|
||||||
|
isHorizontal && css.isHorizontal
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{randomData?.tmnlImgPath ? (
|
||||||
|
<CustomImage
|
||||||
|
delay={0}
|
||||||
|
src={randomData.tmnlImgPath}
|
||||||
|
ariaLabel={randomData.tmnlImgNm}
|
||||||
|
animationSpeed="fast"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<CustomImage
|
||||||
|
delay={0}
|
||||||
|
src={
|
||||||
|
randomData?.vtctpYn === "Y" ? emptyVerImage : emptyHorImage
|
||||||
|
}
|
||||||
|
animationSpeed="fast"
|
||||||
|
ariaLabel={randomData?.tmnlImgNm}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={css.btnPlay}>
|
||||||
|
{randomData?.tmnlImgPath == null ? "" : <img src={btnPlay} alt="play" />}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p className={css.brandIcon}>
|
||||||
|
{randomData?.showId && (
|
||||||
|
<CustomImage
|
||||||
|
delay={0}
|
||||||
|
src={randomData.showId ? randomData.patncLogoPath : null}
|
||||||
|
fallbackSrc={defaultLogoImg}
|
||||||
|
animationSpeed="fast"
|
||||||
|
ariaLabel={randomData.brdcChnlId}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
</SpottableComponent>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default PersistentVideoUnit;
|
||||||
@@ -0,0 +1,641 @@
|
|||||||
|
import React, {
|
||||||
|
useCallback,
|
||||||
|
useEffect,
|
||||||
|
useMemo,
|
||||||
|
useRef,
|
||||||
|
useState,
|
||||||
|
} from "react";
|
||||||
|
|
||||||
|
import classNames from "classnames";
|
||||||
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
|
|
||||||
|
import Spotlight from "@enact/spotlight";
|
||||||
|
import SpotlightContainerDecorator from "@enact/spotlight/SpotlightContainerDecorator";
|
||||||
|
import Spottable from "@enact/spotlight/Spottable";
|
||||||
|
import { getContainerId } from "@enact/spotlight/src/container";
|
||||||
|
|
||||||
|
import btnPlay from "../../../../assets/images/btn/btn-play-thumb-nor.png";
|
||||||
|
import defaultLogoImg from "../../../../assets/images/ic-tab-partners-default@3x.png";
|
||||||
|
import emptyHorImage from "../../../../assets/images/img-home-banner-empty-hor.png";
|
||||||
|
import emptyVerImage from "../../../../assets/images/img-home-banner-empty-ver.png";
|
||||||
|
import defaultImageItem from "../../../../assets/images/img-thumb-empty-product@3x.png";
|
||||||
|
import liveShow from "../../../../assets/images/tag-liveshow.png";
|
||||||
|
import { changeAppStatus } from "../../../actions/commonActions";
|
||||||
|
import { updateHomeInfo } from "../../../actions/homeActions";
|
||||||
|
import {
|
||||||
|
sendLogTopContents,
|
||||||
|
sendLogTotalRecommend,
|
||||||
|
} from "../../../actions/logActions";
|
||||||
|
import { pushPanel } from "../../../actions/panelActions";
|
||||||
|
import {
|
||||||
|
finishVideoPreview,
|
||||||
|
startVideoPlayer,
|
||||||
|
} from "../../../actions/playActions";
|
||||||
|
import CustomImage from "../../../components/CustomImage/CustomImage";
|
||||||
|
import usePriceInfo from "../../../hooks/usePriceInfo";
|
||||||
|
import {
|
||||||
|
LOG_CONTEXT_NAME,
|
||||||
|
LOG_MENU,
|
||||||
|
LOG_MESSAGE_ID,
|
||||||
|
LOG_TP_NO,
|
||||||
|
panel_names,
|
||||||
|
} from "../../../utils/Config";
|
||||||
|
import { $L, formatGMTString } from "../../../utils/helperMethods";
|
||||||
|
import { TEMPLATE_CODE_CONF } from "../HomePanel";
|
||||||
|
import css from "./RandomUnit.new.module.less";
|
||||||
|
|
||||||
|
const SpottableComponent = Spottable("div");
|
||||||
|
|
||||||
|
const Container = SpotlightContainerDecorator(
|
||||||
|
{ enterTo: "last-focused" },
|
||||||
|
"div"
|
||||||
|
);
|
||||||
|
|
||||||
|
export default function RandomUnitNew({
|
||||||
|
bannerData,
|
||||||
|
spotlightId,
|
||||||
|
isHorizontal,
|
||||||
|
handleShelfFocus,
|
||||||
|
randomNumber,
|
||||||
|
onFocus,
|
||||||
|
onBlur,
|
||||||
|
}) {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
const bannerDetailInfos = bannerData.bannerDetailInfos;
|
||||||
|
|
||||||
|
const shptmTmplCd = useSelector(
|
||||||
|
(state) => state.home?.bannerData?.shptmTmplCd
|
||||||
|
);
|
||||||
|
const nowMenu = useSelector((state) => state.common.menu.nowMenu);
|
||||||
|
const entryMenu = useSelector((state) => state.common.menu.entryMenu);
|
||||||
|
|
||||||
|
const homeCategory = useSelector(
|
||||||
|
(state) => state.home.menuData?.data?.homeCategory
|
||||||
|
);
|
||||||
|
const countryCode = useSelector((state) => state.common.httpHeader.cntry_cd);
|
||||||
|
|
||||||
|
const broadcast = useSelector((state) => state.common.broadcast);
|
||||||
|
const { curationId, curationTitle } = useSelector((state) => state.home);
|
||||||
|
const [randomData, setRandomData] = useState("");
|
||||||
|
const [priceInfos, setpriceInfos] = useState("");
|
||||||
|
const [videoError, setVideoError] = useState(false);
|
||||||
|
const [liveIndicies, setLiveIndicies] = useState([]);
|
||||||
|
|
||||||
|
const bannerDataRef = useRef(bannerData);
|
||||||
|
const randomDataRef = useRef(bannerDetailInfos[randomNumber]);
|
||||||
|
|
||||||
|
const topContentsLogInfo = useMemo(() => {
|
||||||
|
if (randomDataRef.current) {
|
||||||
|
const currentRandomData = randomDataRef.current;
|
||||||
|
|
||||||
|
let contId, contNm;
|
||||||
|
|
||||||
|
switch (currentRandomData?.shptmBanrTpCd) {
|
||||||
|
// case: "LIVE" or "VOD"
|
||||||
|
case "DSP00301":
|
||||||
|
case "DSP00302":
|
||||||
|
contId = currentRandomData?.showId;
|
||||||
|
contNm = currentRandomData?.showNm;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// case: "Image Banner"
|
||||||
|
case "DSP00303":
|
||||||
|
contId = currentRandomData?.shptmLnkTpCd;
|
||||||
|
contNm = currentRandomData?.shptmLnkTpNm;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// case: "Today's Deals"
|
||||||
|
default:
|
||||||
|
contId = currentRandomData?.prdtId;
|
||||||
|
contNm = currentRandomData?.prdtNm;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
currentRandomData?.shptmLnkTpCd === "DSP00503" || // "Hot Picks"
|
||||||
|
currentRandomData?.shptmLnkTpCd === "DSP00509" // "Theme"
|
||||||
|
) {
|
||||||
|
contNm = contNm + " | " + currentRandomData?.lnkCurationId;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
banrNo: `${currentRandomData?.banrDpOrd}`,
|
||||||
|
banrTpNm: currentRandomData?.vtctpYn
|
||||||
|
? currentRandomData.vtctpYn === "Y"
|
||||||
|
? "Vertical"
|
||||||
|
: "Horizontal"
|
||||||
|
: "",
|
||||||
|
contId,
|
||||||
|
contNm,
|
||||||
|
contTpNm: currentRandomData?.shptmBanrTpNm ?? "",
|
||||||
|
dspyTpNm: bannerDataRef.current?.shptmDspyTpNm ?? "",
|
||||||
|
expsOrd: bannerDataRef.current?.banrLctnNo ?? "",
|
||||||
|
linkTpCd: "",
|
||||||
|
patncNm: currentRandomData?.patncNm ?? "",
|
||||||
|
patnrId: currentRandomData?.patnrId ?? "",
|
||||||
|
tmplCd: shptmTmplCd,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}, [shptmTmplCd]);
|
||||||
|
|
||||||
|
const sendBannerLog = useCallback(() => {
|
||||||
|
const data = randomDataRef.current;
|
||||||
|
|
||||||
|
if (data && nowMenu === LOG_MENU.HOME_TOP) {
|
||||||
|
dispatch(
|
||||||
|
sendLogTotalRecommend({
|
||||||
|
contextName: LOG_CONTEXT_NAME.HOME,
|
||||||
|
messageId: LOG_MESSAGE_ID.BANNER,
|
||||||
|
curationId,
|
||||||
|
curationTitle,
|
||||||
|
contentType: data.shptmBanrTpNm,
|
||||||
|
contentId: data.showId,
|
||||||
|
contentTitle: data.showNm,
|
||||||
|
productId: data.prdtId,
|
||||||
|
productTitle: data.prdtNm,
|
||||||
|
displayType: "rolling",
|
||||||
|
partner: data.patncNm,
|
||||||
|
brand: data.brndNm, // <- 'brnad' 확인
|
||||||
|
location: data.dspyOrdr,
|
||||||
|
bannerType: data.vtctpYn === "Y" ? "Vertical" : "Horizontal",
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}, [randomDataRef, nowMenu]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (bannerDetailInfos && randomNumber) {
|
||||||
|
const indices = bannerDetailInfos
|
||||||
|
.map((info, index) => (info.shptmBanrTpNm === "LIVE" ? index : null))
|
||||||
|
.filter((index) => index !== null && index !== randomNumber);
|
||||||
|
|
||||||
|
setLiveIndicies(indices);
|
||||||
|
}
|
||||||
|
}, [bannerDetailInfos, randomNumber]);
|
||||||
|
|
||||||
|
const videoErrorClick = useCallback(() => {
|
||||||
|
return dispatch(
|
||||||
|
pushPanel({
|
||||||
|
name: panel_names.FEATURED_BRANDS_PANEL,
|
||||||
|
panelInfo: { from: "gnb", patnrId: randomData.patnrId },
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}, [randomData, dispatch]);
|
||||||
|
|
||||||
|
const shelfFocus = useCallback(() => {
|
||||||
|
if (handleShelfFocus) {
|
||||||
|
handleShelfFocus();
|
||||||
|
}
|
||||||
|
}, [handleShelfFocus]);
|
||||||
|
|
||||||
|
const categoryData = useMemo(() => {
|
||||||
|
if (randomData && randomData.shptmLnkTpCd === "DSP00505") {
|
||||||
|
if (homeCategory && homeCategory.length > 0) {
|
||||||
|
const foundCategory = homeCategory.find(
|
||||||
|
(data) => data.lgCatCd === randomData.lgCatCd
|
||||||
|
);
|
||||||
|
if (foundCategory) {
|
||||||
|
return {
|
||||||
|
lgCatNm: foundCategory.lgCatNm,
|
||||||
|
COUNT: foundCategory.COUNT,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [homeCategory, randomData.shptmLnkTpCd]);
|
||||||
|
|
||||||
|
const imageBannerClick = useCallback(() => {
|
||||||
|
let linkInfo = {};
|
||||||
|
const linkType = randomData.shptmLnkTpCd;
|
||||||
|
|
||||||
|
switch (linkType) {
|
||||||
|
case "DSP00501":
|
||||||
|
linkInfo = {
|
||||||
|
name: panel_names.FEATURED_BRANDS_PANEL,
|
||||||
|
panelInfo: { from: "gnb", patnrId: randomData.patnrId },
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "DSP00502":
|
||||||
|
linkInfo = {
|
||||||
|
name: panel_names.TRENDING_NOW_PANEL,
|
||||||
|
panelInfo: {},
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "DSP00503":
|
||||||
|
linkInfo = {
|
||||||
|
name: panel_names.HOT_PICKS_PANEL,
|
||||||
|
panelInfo: {
|
||||||
|
patnrId: randomData.patnrId,
|
||||||
|
curationId: randomData.lnkCurationId,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "DSP00504":
|
||||||
|
linkInfo = {
|
||||||
|
name: panel_names.ON_SALE_PANEL,
|
||||||
|
panelInfo: {
|
||||||
|
lgCatCd: randomData.lgCatCd,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "DSP00505":
|
||||||
|
if (Object.keys(categoryData).length > 0) {
|
||||||
|
linkInfo = {
|
||||||
|
name: panel_names.CATEGORY_PANEL,
|
||||||
|
panelInfo: {
|
||||||
|
lgCatCd: randomData.lgCatCd,
|
||||||
|
lgCatNm: categoryData.lgCatNm,
|
||||||
|
COUNT: categoryData.COUNT,
|
||||||
|
currentSpot: null,
|
||||||
|
dropDownTab: 0,
|
||||||
|
tab: 0,
|
||||||
|
focusedContainerId: null,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "DSP00506":
|
||||||
|
linkInfo = {
|
||||||
|
name: panel_names.DETAIL_PANEL,
|
||||||
|
panelInfo: {
|
||||||
|
patnrId: randomData.patnrId,
|
||||||
|
prdtId: randomData.prdtId,
|
||||||
|
curationId: randomData.lnkCurationId,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "DSP00507":
|
||||||
|
linkInfo = {
|
||||||
|
patnrId: randomData.patnrId,
|
||||||
|
showId: randomData.showId,
|
||||||
|
shptmBanrTpNm: "VOD",
|
||||||
|
lgCatCd: randomData.lgCatCd,
|
||||||
|
modal: false,
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "DSP00508":
|
||||||
|
linkInfo = {
|
||||||
|
name: panel_names.DETAIL_PANEL,
|
||||||
|
panelInfo: {
|
||||||
|
patnrId: randomData.patnrId,
|
||||||
|
curationId: randomData.lnkCurationId,
|
||||||
|
prdtId: randomData.prdtId,
|
||||||
|
type: "theme",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "DSP00509":
|
||||||
|
linkInfo = {
|
||||||
|
name: panel_names.THEME_CURATION_PANEL,
|
||||||
|
panelInfo: {
|
||||||
|
curationId: randomData.lnkCurationId,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
linkInfo = {
|
||||||
|
name: panel_names.HOME_PANEL,
|
||||||
|
panelInfo: {},
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
let action = linkType === "DSP00507" ? startVideoPlayer : pushPanel;
|
||||||
|
|
||||||
|
dispatch(action(linkInfo));
|
||||||
|
sendBannerLog();
|
||||||
|
dispatch(
|
||||||
|
sendLogTopContents({
|
||||||
|
...topContentsLogInfo,
|
||||||
|
inDt: formatGMTString(new Date()) ?? "",
|
||||||
|
logTpNo: LOG_TP_NO.TOP_CONTENTS.CLICK,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}, [
|
||||||
|
categoryData,
|
||||||
|
dispatch,
|
||||||
|
randomData?.lgCatCd,
|
||||||
|
randomData?.lnkCurationId,
|
||||||
|
randomData?.patnrId,
|
||||||
|
randomData?.prdtId,
|
||||||
|
randomData?.showId,
|
||||||
|
randomData?.shptmLnkTpCd,
|
||||||
|
topContentsLogInfo,
|
||||||
|
]);
|
||||||
|
|
||||||
|
const todayDealClick = useCallback(() => {
|
||||||
|
dispatch(
|
||||||
|
pushPanel({
|
||||||
|
name: panel_names.DETAIL_PANEL,
|
||||||
|
panelInfo: {
|
||||||
|
patnrId: randomData.patnrId,
|
||||||
|
prdtId: randomData.prdtId,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
sendBannerLog();
|
||||||
|
|
||||||
|
dispatch(
|
||||||
|
sendLogTopContents({
|
||||||
|
...topContentsLogInfo,
|
||||||
|
inDt: formatGMTString(new Date()) ?? "",
|
||||||
|
logTpNo: LOG_TP_NO.TOP_CONTENTS.CLICK,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}, [
|
||||||
|
dispatch,
|
||||||
|
randomData?.patnrId,
|
||||||
|
randomData?.prdtId,
|
||||||
|
randomDataRef,
|
||||||
|
topContentsLogInfo,
|
||||||
|
]);
|
||||||
|
|
||||||
|
const videoClick = useCallback(() => {
|
||||||
|
const lastFocusedTargetId = getContainerId(Spotlight.getCurrent());
|
||||||
|
const currentSpot = Spotlight.getCurrent();
|
||||||
|
|
||||||
|
if (lastFocusedTargetId) {
|
||||||
|
dispatch(
|
||||||
|
updateHomeInfo({
|
||||||
|
name: panel_names.HOME_PANEL,
|
||||||
|
panelInfo: {
|
||||||
|
lastFocusedTargetId,
|
||||||
|
focusedContainerId: TEMPLATE_CODE_CONF.TOP,
|
||||||
|
currentSpot: currentSpot?.getAttribute("data-spotlight-id"),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch(
|
||||||
|
startVideoPlayer({
|
||||||
|
showUrl: randomData.showUrl,
|
||||||
|
patnrId: randomData.patnrId,
|
||||||
|
showId: randomData.showId,
|
||||||
|
shptmBanrTpNm: randomData.showId ? randomData.shptmBanrTpNm : "MEDIA",
|
||||||
|
lgCatCd: randomData.lgCatCd,
|
||||||
|
chanId: randomData.brdcChnlId,
|
||||||
|
modal: false,
|
||||||
|
modalContainerId: spotlightId,
|
||||||
|
modalClassName: css.videoModal,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
sendBannerLog();
|
||||||
|
|
||||||
|
dispatch(
|
||||||
|
sendLogTopContents({
|
||||||
|
...topContentsLogInfo,
|
||||||
|
inDt: formatGMTString(new Date()) ?? "",
|
||||||
|
logTpNo: LOG_TP_NO.TOP_CONTENTS.CLICK,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
if (onBlur) {
|
||||||
|
onBlur();
|
||||||
|
}
|
||||||
|
}, [randomData, spotlightId, topContentsLogInfo, nowMenu, randomDataRef, onBlur]);
|
||||||
|
|
||||||
|
const { originalPrice, discountedPrice, discountRate, offerInfo } =
|
||||||
|
usePriceInfo(priceInfos) || {};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let _nowMenu = nowMenu;
|
||||||
|
let _entryMenu = entryMenu;
|
||||||
|
|
||||||
|
if (nowMenu === LOG_MENU.HOME_TOP) {
|
||||||
|
const params = {
|
||||||
|
...topContentsLogInfo,
|
||||||
|
entryMenu: _entryMenu,
|
||||||
|
inDt: formatGMTString(new Date()) ?? "",
|
||||||
|
logTpNo: LOG_TP_NO.TOP_CONTENTS.VIEW,
|
||||||
|
nowMenu: _nowMenu,
|
||||||
|
};
|
||||||
|
|
||||||
|
return () => dispatch(sendLogTopContents(params));
|
||||||
|
}
|
||||||
|
}, [dispatch, entryMenu, nowMenu, topContentsLogInfo]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
sendBannerLog();
|
||||||
|
}, [randomDataRef, nowMenu]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (bannerData) {
|
||||||
|
setRandomData(bannerDetailInfos[randomNumber]);
|
||||||
|
}
|
||||||
|
}, [bannerData, dispatch, randomNumber]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (randomData && randomData.priceInfo !== null) {
|
||||||
|
return setpriceInfos(randomData.priceInfo);
|
||||||
|
}
|
||||||
|
}, [randomData]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (broadcast?.type === "videoError") {
|
||||||
|
setVideoError(true);
|
||||||
|
if (liveIndicies.length > 0) {
|
||||||
|
const nextIndex = liveIndicies[0];
|
||||||
|
|
||||||
|
setLiveIndicies((prev) => prev.slice(1));
|
||||||
|
setRandomData(bannerDetailInfos[nextIndex]);
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
setVideoError(false);
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [broadcast, liveIndicies, bannerDetailInfos]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Container
|
||||||
|
className={classNames(
|
||||||
|
css.rollingWrap,
|
||||||
|
isHorizontal && css.isHorizontalWrap
|
||||||
|
)}
|
||||||
|
onFocus={shelfFocus}
|
||||||
|
>
|
||||||
|
{randomData?.shptmBanrTpNm == "Image Banner" ? (
|
||||||
|
<SpottableComponent
|
||||||
|
className={classNames(
|
||||||
|
css.itemBox,
|
||||||
|
isHorizontal && css.isHorizontal
|
||||||
|
)}
|
||||||
|
onClick={imageBannerClick}
|
||||||
|
spotlightId={spotlightId}
|
||||||
|
aria-label={
|
||||||
|
randomData.prdtNm ? randomData.prdtNm : randomData.tmnlImgNm
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div className={css.imgBanner}>
|
||||||
|
<CustomImage
|
||||||
|
delay={0}
|
||||||
|
src={randomData.tmnlImgPath}
|
||||||
|
animationSpeed="fast"
|
||||||
|
ariaLabel={randomData.tmnImgNm}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</SpottableComponent>
|
||||||
|
) : randomData?.shptmBanrTpNm == "LIVE" ||
|
||||||
|
randomData?.shptmBanrTpNm == "VOD" ? (
|
||||||
|
<SpottableComponent
|
||||||
|
className={classNames(
|
||||||
|
css.itemBox,
|
||||||
|
isHorizontal && css.isHorizontal
|
||||||
|
)}
|
||||||
|
onClick={videoError === true ? videoErrorClick : videoClick}
|
||||||
|
onFocus={onFocus}
|
||||||
|
onBlur={onBlur}
|
||||||
|
spotlightId={spotlightId}
|
||||||
|
aria-label={
|
||||||
|
randomData.shptmBanrTpNm == "LIVE"
|
||||||
|
? "LIVE " + randomData.showNm
|
||||||
|
: randomData.showNm
|
||||||
|
}
|
||||||
|
alt={"LIVE"}
|
||||||
|
>
|
||||||
|
{randomData.shptmBanrTpNm == "LIVE" && videoError === false && (
|
||||||
|
<p className={css.liveIcon}>
|
||||||
|
<CustomImage
|
||||||
|
delay={0}
|
||||||
|
src={liveShow}
|
||||||
|
animationSpeed="fast"
|
||||||
|
ariaLabel="LIVE icon"
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{videoError === true && (
|
||||||
|
<div className={css.errorContents}>
|
||||||
|
<div>
|
||||||
|
{randomData.patncLogoPath && (
|
||||||
|
<img
|
||||||
|
className={css.errorlogo}
|
||||||
|
src={randomData.patncLogoPath}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<p className={css.errorText}>
|
||||||
|
{$L("Click the screen to see more products!")}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{videoError === false && (
|
||||||
|
<div
|
||||||
|
className={classNames(
|
||||||
|
css.itemBox,
|
||||||
|
isHorizontal && css.isHorizontal
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{randomData.tmnlImgPath ? (
|
||||||
|
<CustomImage
|
||||||
|
delay={0}
|
||||||
|
src={randomData.tmnlImgPath}
|
||||||
|
ariaLabel={randomData.tmnImgNm}
|
||||||
|
animationSpeed="fast"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<CustomImage
|
||||||
|
delay={0}
|
||||||
|
src={
|
||||||
|
randomData.vtctpYn === "Y" ? emptyVerImage : emptyHorImage
|
||||||
|
}
|
||||||
|
animationSpeed="fast"
|
||||||
|
ariaLabel={randomData.tmnImgNm}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{videoError === false && (
|
||||||
|
<div className={css.btnPlay}>
|
||||||
|
{randomData.tmnlImgPath == null ? "" : <img src={btnPlay} />}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{videoError === false && (
|
||||||
|
<p className={css.brandIcon}>
|
||||||
|
{randomData.showId && (
|
||||||
|
<CustomImage
|
||||||
|
delay={0}
|
||||||
|
src={randomData.showId ? randomData.patncLogoPath : null}
|
||||||
|
fallbackSrc={defaultLogoImg}
|
||||||
|
animationSpeed="fast"
|
||||||
|
ariaLabel={randomData.brdcChnlId}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</SpottableComponent>
|
||||||
|
) : randomData?.shptmBanrTpNm == "Today's Deals" ? (
|
||||||
|
<SpottableComponent
|
||||||
|
className={classNames(
|
||||||
|
css.itemBox,
|
||||||
|
css.todaysDeals,
|
||||||
|
countryCode === "RU" ? css.ru : "",
|
||||||
|
countryCode === "DE" ? css.de : "",
|
||||||
|
isHorizontal && css.isHorizontal
|
||||||
|
)}
|
||||||
|
onClick={todayDealClick}
|
||||||
|
spotlightId={spotlightId}
|
||||||
|
aria-label={
|
||||||
|
randomData.prdtNm ? randomData.prdtNm : randomData.tmnlImgNm
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div className={css.productInfo}>
|
||||||
|
<div className={css.todaysDealTitle}>{$L("TODAY's DEALS")}</div>
|
||||||
|
<div
|
||||||
|
className={css.textBox}
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: `${randomData.prdtNm}`,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<div className={css.accBox}>
|
||||||
|
{parseFloat(originalPrice?.replace("$", "")) === 0
|
||||||
|
? randomData?.offerInfo
|
||||||
|
: discountRate
|
||||||
|
? discountedPrice
|
||||||
|
: originalPrice}
|
||||||
|
{discountRate && !isHorizontal && (
|
||||||
|
<span className={css.saleAccBox}>{originalPrice}</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
{isHorizontal &&
|
||||||
|
parseFloat(originalPrice?.replace("$", "")) !== 0 && (
|
||||||
|
<span className={css.saleAccBox}>{originalPrice}</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={css.itemImgBox}>
|
||||||
|
<CustomImage
|
||||||
|
delay={0}
|
||||||
|
src={randomData.tmnlImgPath}
|
||||||
|
animationSpeed="fast"
|
||||||
|
fallbackSrc={defaultImageItem}
|
||||||
|
ariaLabel={randomData.tmnlImgNm}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</SpottableComponent>
|
||||||
|
) : null}
|
||||||
|
</Container>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,281 @@
|
|||||||
|
@import "../../../style/CommonStyle.module.less";
|
||||||
|
@import "../../../style/utils.module.less";
|
||||||
|
|
||||||
|
.rollingWrap {
|
||||||
|
position: relative;
|
||||||
|
.itemBox {
|
||||||
|
.size(@w: 486px, @h: 858px);
|
||||||
|
position: relative;
|
||||||
|
text-align: center;
|
||||||
|
.brandIcon {
|
||||||
|
overflow: hidden;
|
||||||
|
position: absolute;
|
||||||
|
right: 30px;
|
||||||
|
bottom: 30px;
|
||||||
|
> img {
|
||||||
|
border-radius: 0;
|
||||||
|
.size(@w: 60px, @h: 60px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.liveIcon {
|
||||||
|
z-index: 2;
|
||||||
|
position: absolute;
|
||||||
|
left: 18px;
|
||||||
|
top: 18px;
|
||||||
|
> img {
|
||||||
|
.size(@w: 108px, @h: 48px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.imgBanner {
|
||||||
|
> img {
|
||||||
|
border-radius: 10px;
|
||||||
|
.size(@w: 486px, @h: 858px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.btnPlay {
|
||||||
|
.size(@w: 100%, @h: 100%);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
z-index: 2;
|
||||||
|
> img {
|
||||||
|
.size(@w: 120px, @h: 120px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.isHorizontal {
|
||||||
|
.size(@w: 744px, @h: 420px);
|
||||||
|
.imgBanner {
|
||||||
|
> img {
|
||||||
|
border-radius: 10px;
|
||||||
|
.size(@w: 744px, @h: 420px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.errorContents {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
.errorlogo {
|
||||||
|
width: 120px;
|
||||||
|
height: 120px;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
.errorText {
|
||||||
|
margin-top: 30px;
|
||||||
|
color: #ff0000;
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.todaysDeals {
|
||||||
|
background-image: url(../../../../assets/images/img-home-banner-td-ver@3x.png);
|
||||||
|
background-size: 486px 858px;
|
||||||
|
background-position: left top;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 73px 36px 0;
|
||||||
|
&.ru {
|
||||||
|
.productInfo {
|
||||||
|
.todaysDealTitle {
|
||||||
|
font-size: 58px;
|
||||||
|
line-height: 60px;
|
||||||
|
font-family: @arialFontBold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.de {
|
||||||
|
.productInfo {
|
||||||
|
.todaysDealTitle {
|
||||||
|
font-size: 59px !important;
|
||||||
|
line-height: 63px !important;
|
||||||
|
letter-spacing: -1px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.productInfo {
|
||||||
|
margin-bottom: 33px;
|
||||||
|
.todaysDealTitle {
|
||||||
|
.size(@w:100%,@h:132px);
|
||||||
|
font-size: 76px;
|
||||||
|
word-break: break-word;
|
||||||
|
font-stretch: normal;
|
||||||
|
color: #151515;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 76px;
|
||||||
|
font-family: @arialFontBold;
|
||||||
|
}
|
||||||
|
.textBox {
|
||||||
|
.size(@w: 100%, @h: 80px);
|
||||||
|
margin-top: 71px;
|
||||||
|
.elip(@clamp:2);
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 30px;
|
||||||
|
color: @COLOR_GRAY06;
|
||||||
|
line-height: 1.27;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
}
|
||||||
|
.accBox {
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 42px;
|
||||||
|
color: @PRIMARY_COLOR_RED;
|
||||||
|
line-height: 1.14;
|
||||||
|
display: inline-block;
|
||||||
|
.elip(@clamp:1);
|
||||||
|
> strong {
|
||||||
|
width: 260px;
|
||||||
|
font-size: 30px;
|
||||||
|
line-height: 1.27;
|
||||||
|
display: block;
|
||||||
|
.elip(@clamp:2);
|
||||||
|
}
|
||||||
|
.saleAccBox {
|
||||||
|
font-weight: normal;
|
||||||
|
font-size: 24px;
|
||||||
|
color: @COLOR_GRAY04;
|
||||||
|
vertical-align: middle;
|
||||||
|
text-decoration: line-through;
|
||||||
|
margin-left: 9px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.itemImgBox {
|
||||||
|
> img {
|
||||||
|
.size(@w: 356px, @h: 356px);
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.isHorizontal {
|
||||||
|
background-image: url(../../../../assets/images/img-home-banner-td-hor@3x.png);
|
||||||
|
background-size: 744px 420px;
|
||||||
|
background-position: center center;
|
||||||
|
display: flex;
|
||||||
|
padding: 0 30px 0 0;
|
||||||
|
border-radius: 10px;
|
||||||
|
-webkit-border-radius: 10px;
|
||||||
|
-moz-border-radius: 10px;
|
||||||
|
-o-border-radius: 10px;
|
||||||
|
> div {
|
||||||
|
flex: none;
|
||||||
|
}
|
||||||
|
&.ru {
|
||||||
|
.productInfo {
|
||||||
|
.todaysDealTitle {
|
||||||
|
font-size: 58px;
|
||||||
|
line-height: 60px;
|
||||||
|
font-family: @arialFontBold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.de {
|
||||||
|
.productInfo {
|
||||||
|
.todaysDealTitle {
|
||||||
|
font-size: 59px !important;
|
||||||
|
line-height: 63px !important;
|
||||||
|
letter-spacing: -2px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.productInfo {
|
||||||
|
margin-bottom: 0;
|
||||||
|
.todaysDealTitle {
|
||||||
|
.size(@w:305px,@h:114px);
|
||||||
|
margin-top: 53px;
|
||||||
|
margin-left: 49px;
|
||||||
|
font-size: 66px;
|
||||||
|
word-break: break-word;
|
||||||
|
color: #151515;
|
||||||
|
text-align: left;
|
||||||
|
line-height: 57px;
|
||||||
|
font-family: @arialBlack;
|
||||||
|
}
|
||||||
|
.textBox {
|
||||||
|
.size(@w: 294px, @h: 80px);
|
||||||
|
margin: 67px 0 5px 50px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
.accBox {
|
||||||
|
.size(@w: 320px, @h: 50px);
|
||||||
|
margin-left: 50px;
|
||||||
|
text-align: left;
|
||||||
|
display: block;
|
||||||
|
.elip(@clamp:1);
|
||||||
|
}
|
||||||
|
.saleAccBox {
|
||||||
|
color: #767676;
|
||||||
|
display: block;
|
||||||
|
text-align: left;
|
||||||
|
margin: 5px 0 0 55px;
|
||||||
|
text-decoration: line-through;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.itemImgBox {
|
||||||
|
.position(@position: absolute, @top: 47px, @left: 389px);
|
||||||
|
.size(@w: 326px, @h: 326px);
|
||||||
|
> img {
|
||||||
|
.size(@w: inherit, @h: inherit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&:focus {
|
||||||
|
&::after {
|
||||||
|
.focused(@boxShadow:22px, @borderRadius: 12px);
|
||||||
|
border: 6px solid @PRIMARY_COLOR_RED;
|
||||||
|
top: -4px;
|
||||||
|
right: -4px;
|
||||||
|
bottom: -4px;
|
||||||
|
left: -4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.arrow {
|
||||||
|
z-index: 9999;
|
||||||
|
.size(@w: 42px, @h: 42px);
|
||||||
|
background-size: 42px 42px;
|
||||||
|
background-position: center center;
|
||||||
|
&.leftBtn {
|
||||||
|
.position(@position: absolute, @top: 406px, @left: 18px);
|
||||||
|
background-image: url("../../../../assets/images/btn/btn_prev_thumb_nor.png");
|
||||||
|
&:focus {
|
||||||
|
background-image: url("../../../../assets/images/btn/btn_prev_thumb_foc.png");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.rightBtn {
|
||||||
|
.position(@position: absolute, @top: 406px, @right: 18px);
|
||||||
|
background-image: url("../../../../assets/images/btn/btn_next_thumb_nor.png");
|
||||||
|
&:focus {
|
||||||
|
background-image: url("../../../../assets/images/btn/btn_next_thumb_foc.png");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.isHorizontalWrap {
|
||||||
|
border-radius: 10px;
|
||||||
|
.arrow {
|
||||||
|
&.leftBtn {
|
||||||
|
.position(@position: absolute, @top: 189px, @left: 18px);
|
||||||
|
}
|
||||||
|
&.rightBtn {
|
||||||
|
.position(@position: absolute, @top: 189px, @right: 18px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.videoModal {
|
||||||
|
&::after {
|
||||||
|
.focused(@boxShadow:0, @borderRadius: 12px);
|
||||||
|
border: 6px solid @PRIMARY_COLOR_RED;
|
||||||
|
top: -4px;
|
||||||
|
right: -4px;
|
||||||
|
bottom: -4px;
|
||||||
|
left: -4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -75,43 +75,43 @@ const HomePanel = ({ isOnTop }) => {
|
|||||||
const isGnbOpened = useSelector((state) => state.common.isGnbOpened);
|
const isGnbOpened = useSelector((state) => state.common.isGnbOpened);
|
||||||
const homeLayoutInfo = useSelector((state) => state.home.layoutData);
|
const homeLayoutInfo = useSelector((state) => state.home.layoutData);
|
||||||
const panelInfo = useSelector(
|
const panelInfo = useSelector(
|
||||||
(state) => state.home.homeInfo?.panelInfo ?? {}
|
(state) => state.home.homeInfo?.panelInfo ?? {},
|
||||||
);
|
);
|
||||||
const panels = useSelector((state) => state.panels.panels);
|
const panels = useSelector((state) => state.panels.panels);
|
||||||
const webOSVersion = useSelector(
|
const webOSVersion = useSelector(
|
||||||
(state) => state.common.appStatus?.webOSVersion
|
(state) => state.common.appStatus?.webOSVersion,
|
||||||
);
|
);
|
||||||
const enterThroughGNB = useSelector((state) => state.home.enterThroughGNB);
|
const enterThroughGNB = useSelector((state) => state.home.enterThroughGNB);
|
||||||
const defaultFocus = useSelector((state) => state.home.defaultFocus);
|
const defaultFocus = useSelector((state) => state.home.defaultFocus);
|
||||||
|
|
||||||
const categoryInfos = useSelector(
|
const categoryInfos = useSelector(
|
||||||
(state) => state.onSale.homeOnSaleData?.data?.categoryInfos
|
(state) => state.onSale.homeOnSaleData?.data?.categoryInfos,
|
||||||
);
|
);
|
||||||
|
|
||||||
const categoryItemInfos = useSelector(
|
const categoryItemInfos = useSelector(
|
||||||
(state) => state.main.subCategoryData?.categoryItemInfos
|
(state) => state.main.subCategoryData?.categoryItemInfos,
|
||||||
);
|
);
|
||||||
|
|
||||||
const { popupVisible, activePopup } = useSelector(
|
const { popupVisible, activePopup } = useSelector(
|
||||||
(state) => state.common.popup
|
(state) => state.common.popup,
|
||||||
);
|
);
|
||||||
|
|
||||||
const eventPopInfosData = useSelector(
|
const eventPopInfosData = useSelector(
|
||||||
(state) => state.event.eventData.eventPopInfo
|
(state) => state.event.eventData.eventPopInfo,
|
||||||
);
|
);
|
||||||
const eventData = useSelector((state) => state.event.eventData);
|
const eventData = useSelector((state) => state.event.eventData);
|
||||||
const eventClickSuccess = useSelector(
|
const eventClickSuccess = useSelector(
|
||||||
(state) => state.event.eventClickSuccess
|
(state) => state.event.eventClickSuccess,
|
||||||
);
|
);
|
||||||
const homeOnSaleInfos = useSelector(
|
const homeOnSaleInfos = useSelector(
|
||||||
(state) => state.onSale.homeOnSaleData?.data.homeOnSaleInfos
|
(state) => state.onSale.homeOnSaleData?.data.homeOnSaleInfos,
|
||||||
);
|
);
|
||||||
const bestSellerDatas = useSelector(
|
const bestSellerDatas = useSelector(
|
||||||
(state) => state.product.bestSellerData?.bestSeller
|
(state) => state.product.bestSellerData?.bestSeller,
|
||||||
);
|
);
|
||||||
const topInfos = useSelector((state) => state.main.top20ShowData.topInfos);
|
const topInfos = useSelector((state) => state.main.top20ShowData.topInfos);
|
||||||
const isDeepLink = useSelector(
|
const isDeepLink = useSelector(
|
||||||
(state) => state.common.deepLinkInfo.isDeepLink
|
(state) => state.common.deepLinkInfo.isDeepLink,
|
||||||
);
|
);
|
||||||
|
|
||||||
const [btnDisabled, setBtnDisabled] = useState(true);
|
const [btnDisabled, setBtnDisabled] = useState(true);
|
||||||
@@ -120,13 +120,13 @@ const HomePanel = ({ isOnTop }) => {
|
|||||||
const [eventPopOpen, setEventPopOpen] = useState(false);
|
const [eventPopOpen, setEventPopOpen] = useState(false);
|
||||||
const [nowShelf, setNowShelf] = useState(panelInfo.nowShelf);
|
const [nowShelf, setNowShelf] = useState(panelInfo.nowShelf);
|
||||||
const [firstLgCatCd, setFirstLgCatCd] = useState(
|
const [firstLgCatCd, setFirstLgCatCd] = useState(
|
||||||
panelInfo.currentCatCd ?? null
|
panelInfo.currentCatCd ?? null,
|
||||||
);
|
);
|
||||||
const [cateCd, setCateCd] = useState(panelInfo.currentCatCd ?? null);
|
const [cateCd, setCateCd] = useState(panelInfo.currentCatCd ?? null);
|
||||||
const [cateNm, setCateNm] = useState(panelInfo.currentCateName ?? null);
|
const [cateNm, setCateNm] = useState(panelInfo.currentCateName ?? null);
|
||||||
const { entryMenu, nowMenu } = useSelector((state) => state.common.menu);
|
const { entryMenu, nowMenu } = useSelector((state) => state.common.menu);
|
||||||
const [focusedContainerId, setFocusedContainerId] = useState(
|
const [focusedContainerId, setFocusedContainerId] = useState(
|
||||||
panelInfo.focusedContainerId
|
panelInfo.focusedContainerId,
|
||||||
);
|
);
|
||||||
|
|
||||||
const isInitialRender = useRef(true);
|
const isInitialRender = useRef(true);
|
||||||
@@ -139,7 +139,7 @@ const HomePanel = ({ isOnTop }) => {
|
|||||||
sendLogTotalRecommend({
|
sendLogTotalRecommend({
|
||||||
messageId: LOG_MESSAGE_ID.HOME,
|
messageId: LOG_MESSAGE_ID.HOME,
|
||||||
contextName: LOG_CONTEXT_NAME.HOME,
|
contextName: LOG_CONTEXT_NAME.HOME,
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}, [entryMenu, nowMenu]);
|
}, [entryMenu, nowMenu]);
|
||||||
@@ -147,7 +147,7 @@ const HomePanel = ({ isOnTop }) => {
|
|||||||
const sortedHomeLayoutInfo = useMemo(() => {
|
const sortedHomeLayoutInfo = useMemo(() => {
|
||||||
if (homeLayoutInfo && homeLayoutInfo.homeLayoutInfo) {
|
if (homeLayoutInfo && homeLayoutInfo.homeLayoutInfo) {
|
||||||
const sorted = [...homeLayoutInfo.homeLayoutInfo].sort(
|
const sorted = [...homeLayoutInfo.homeLayoutInfo].sort(
|
||||||
(x, y) => x.expsOrd - y.expsOrd
|
(x, y) => x.expsOrd - y.expsOrd,
|
||||||
);
|
);
|
||||||
return sorted;
|
return sorted;
|
||||||
}
|
}
|
||||||
@@ -183,7 +183,7 @@ const HomePanel = ({ isOnTop }) => {
|
|||||||
panelInfo: {
|
panelInfo: {
|
||||||
currentSpot: currentSpot,
|
currentSpot: currentSpot,
|
||||||
},
|
},
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
dispatch(setShowPopup(ACTIVE_POPUP.exitPopup));
|
dispatch(setShowPopup(ACTIVE_POPUP.exitPopup));
|
||||||
@@ -197,7 +197,7 @@ const HomePanel = ({ isOnTop }) => {
|
|||||||
contextName: LOG_CONTEXT_NAME.SHOPTIME,
|
contextName: LOG_CONTEXT_NAME.SHOPTIME,
|
||||||
messageId: LOG_MESSAGE_ID.VIEW_CHANGE,
|
messageId: LOG_MESSAGE_ID.VIEW_CHANGE,
|
||||||
visible: false,
|
visible: false,
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
}, [dispatch]);
|
}, [dispatch]);
|
||||||
|
|
||||||
@@ -261,7 +261,7 @@ const HomePanel = ({ isOnTop }) => {
|
|||||||
shelfLocation: location,
|
shelfLocation: location,
|
||||||
shelfId: containerId,
|
shelfId: containerId,
|
||||||
shelfTitle: title,
|
shelfTitle: title,
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
setNowShelf(containerId);
|
setNowShelf(containerId);
|
||||||
@@ -271,14 +271,14 @@ const HomePanel = ({ isOnTop }) => {
|
|||||||
currentSentMenuRef.current = nowMenu;
|
currentSentMenuRef.current = nowMenu;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[pageSpotIds, nowShelf, panelInfo.nowShelf]
|
[pageSpotIds, nowShelf, panelInfo.nowShelf],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleItemFocus = useCallback(
|
const handleItemFocus = useCallback(
|
||||||
(containerId, location, title) => () => {
|
(containerId, location, title) => () => {
|
||||||
doSendLogGNB(containerId, location, title);
|
doSendLogGNB(containerId, location, title);
|
||||||
},
|
},
|
||||||
[doSendLogGNB]
|
[doSendLogGNB],
|
||||||
);
|
);
|
||||||
|
|
||||||
const renderPageItem = useCallback(() => {
|
const renderPageItem = useCallback(() => {
|
||||||
@@ -298,7 +298,7 @@ const HomePanel = ({ isOnTop }) => {
|
|||||||
handleShelfFocus={handleItemFocus(
|
handleShelfFocus={handleItemFocus(
|
||||||
el.shptmApphmDspyOptCd,
|
el.shptmApphmDspyOptCd,
|
||||||
el.expsOrd,
|
el.expsOrd,
|
||||||
el.shptmApphmDspyOptNm
|
el.shptmApphmDspyOptNm,
|
||||||
)}
|
)}
|
||||||
handleItemFocus={handleItemFocus(el.shptmApphmDspyOptCd)}
|
handleItemFocus={handleItemFocus(el.shptmApphmDspyOptCd)}
|
||||||
/>
|
/>
|
||||||
@@ -320,7 +320,7 @@ const HomePanel = ({ isOnTop }) => {
|
|||||||
handleShelfFocus={handleItemFocus(
|
handleShelfFocus={handleItemFocus(
|
||||||
el.shptmApphmDspyOptCd,
|
el.shptmApphmDspyOptCd,
|
||||||
el.expsOrd,
|
el.expsOrd,
|
||||||
el.shptmApphmDspyOptNm
|
el.shptmApphmDspyOptNm,
|
||||||
)}
|
)}
|
||||||
handleItemFocus={handleItemFocus(el.shptmApphmDspyOptCd)}
|
handleItemFocus={handleItemFocus(el.shptmApphmDspyOptCd)}
|
||||||
shelfLocation={el.expsOrd}
|
shelfLocation={el.expsOrd}
|
||||||
@@ -338,7 +338,7 @@ const HomePanel = ({ isOnTop }) => {
|
|||||||
handleShelfFocus={handleItemFocus(
|
handleShelfFocus={handleItemFocus(
|
||||||
el.shptmApphmDspyOptCd,
|
el.shptmApphmDspyOptCd,
|
||||||
el.expsOrd,
|
el.expsOrd,
|
||||||
el.shptmApphmDspyOptNm
|
el.shptmApphmDspyOptNm,
|
||||||
)}
|
)}
|
||||||
handleItemFocus={handleItemFocus(el.shptmApphmDspyOptCd)}
|
handleItemFocus={handleItemFocus(el.shptmApphmDspyOptCd)}
|
||||||
shelfLocation={el.expsOrd}
|
shelfLocation={el.expsOrd}
|
||||||
@@ -356,7 +356,7 @@ const HomePanel = ({ isOnTop }) => {
|
|||||||
handleShelfFocus={handleItemFocus(
|
handleShelfFocus={handleItemFocus(
|
||||||
el.shptmApphmDspyOptCd,
|
el.shptmApphmDspyOptCd,
|
||||||
el.expsOrd,
|
el.expsOrd,
|
||||||
el.shptmApphmDspyOptNm
|
el.shptmApphmDspyOptNm,
|
||||||
)}
|
)}
|
||||||
handleItemFocus={handleItemFocus(el.shptmApphmDspyOptCd)}
|
handleItemFocus={handleItemFocus(el.shptmApphmDspyOptCd)}
|
||||||
shelfLocation={el.expsOrd}
|
shelfLocation={el.expsOrd}
|
||||||
@@ -374,7 +374,7 @@ const HomePanel = ({ isOnTop }) => {
|
|||||||
handleShelfFocus={handleItemFocus(
|
handleShelfFocus={handleItemFocus(
|
||||||
el.shptmApphmDspyOptCd,
|
el.shptmApphmDspyOptCd,
|
||||||
el.expsOrd,
|
el.expsOrd,
|
||||||
el.shptmApphmDspyOptNm
|
el.shptmApphmDspyOptNm,
|
||||||
)}
|
)}
|
||||||
handleItemFocus={handleItemFocus(el.shptmApphmDspyOptCd)}
|
handleItemFocus={handleItemFocus(el.shptmApphmDspyOptCd)}
|
||||||
shelfLocation={el.expsOrd}
|
shelfLocation={el.expsOrd}
|
||||||
@@ -473,7 +473,7 @@ const HomePanel = ({ isOnTop }) => {
|
|||||||
cbChangePageRef,
|
cbChangePageRef,
|
||||||
dispatch,
|
dispatch,
|
||||||
isOnTop,
|
isOnTop,
|
||||||
]
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
const bestSellerLoaded = useCallback(() => {
|
const bestSellerLoaded = useCallback(() => {
|
||||||
@@ -486,7 +486,7 @@ const HomePanel = ({ isOnTop }) => {
|
|||||||
|
|
||||||
if (isDeepLink || (!panels.length && !panelInfo.focusedContainerId)) {
|
if (isDeepLink || (!panels.length && !panelInfo.focusedContainerId)) {
|
||||||
dispatch(
|
dispatch(
|
||||||
changeAppStatus({ showLoadingPanel: { show: true, type: "wait" } })
|
changeAppStatus({ showLoadingPanel: { show: true, type: "wait" } }),
|
||||||
);
|
);
|
||||||
dispatch(getHomeMainContents());
|
dispatch(getHomeMainContents());
|
||||||
dispatch(getHomeLayout());
|
dispatch(getHomeLayout());
|
||||||
@@ -495,7 +495,7 @@ const HomePanel = ({ isOnTop }) => {
|
|||||||
homeSaleInfosIncFlag: "Y",
|
homeSaleInfosIncFlag: "Y",
|
||||||
categoryIncFlag: "Y",
|
categoryIncFlag: "Y",
|
||||||
saleInfosIncFlag: "N",
|
saleInfosIncFlag: "N",
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
dispatch(getTop20Show());
|
dispatch(getTop20Show());
|
||||||
dispatch(getBestSeller(bestSellerLoaded));
|
dispatch(getBestSeller(bestSellerLoaded));
|
||||||
@@ -528,8 +528,8 @@ const HomePanel = ({ isOnTop }) => {
|
|||||||
tabType: "CAT00102",
|
tabType: "CAT00102",
|
||||||
filterType: "CAT00202",
|
filterType: "CAT00202",
|
||||||
},
|
},
|
||||||
1
|
1,
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}, [categoryInfos, firstLgCatCd, dispatch]);
|
}, [categoryInfos, firstLgCatCd, dispatch]);
|
||||||
@@ -544,7 +544,7 @@ const HomePanel = ({ isOnTop }) => {
|
|||||||
} else setEventPopOpen(true);
|
} else setEventPopOpen(true);
|
||||||
} else setEventPopOpen(false);
|
} else setEventPopOpen(false);
|
||||||
},
|
},
|
||||||
[webOSVersion]
|
[webOSVersion],
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -583,7 +583,7 @@ const HomePanel = ({ isOnTop }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const tBody = document.querySelector(
|
const tBody = document.querySelector(
|
||||||
`[data-spotlight-id="${SpotlightIds.HOME_TBODY}"]`
|
`[data-spotlight-id="${SpotlightIds.HOME_TBODY}"]`,
|
||||||
);
|
);
|
||||||
const currentSpot = c && tBody.contains(c) ? targetSpotlightId : null;
|
const currentSpot = c && tBody.contains(c) ? targetSpotlightId : null;
|
||||||
|
|
||||||
@@ -597,7 +597,7 @@ const HomePanel = ({ isOnTop }) => {
|
|||||||
currentCateName: targetSpotlightCateNm,
|
currentCateName: targetSpotlightCateNm,
|
||||||
focusedContainerId: focusedContainerIdRef.current,
|
focusedContainerId: focusedContainerIdRef.current,
|
||||||
},
|
},
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
}, [dispatch]);
|
}, [dispatch]);
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
// src: views/IntroPanel/IntroPanel.new.jsx
|
// src: views/IntroPanel/IntroPanel.new.jsx
|
||||||
|
|
||||||
import React, { useCallback, useEffect, useState, useMemo, useRef } from "react";
|
import React, {
|
||||||
import { flushSync } from "react-dom";
|
useCallback,
|
||||||
|
useEffect,
|
||||||
|
useMemo,
|
||||||
|
useRef,
|
||||||
|
useState,
|
||||||
|
} from "react";
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
|
|
||||||
import Region from "@enact/sandstone/Region";
|
import Region from "@enact/sandstone/Region";
|
||||||
import Spotlight from "@enact/spotlight";
|
import Spotlight from "@enact/spotlight";
|
||||||
import SpotlightContainerDecorator from "@enact/spotlight/SpotlightContainerDecorator";
|
import SpotlightContainerDecorator from "@enact/spotlight/SpotlightContainerDecorator";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
setExitApp,
|
setExitApp,
|
||||||
setHidePopup,
|
setHidePopup,
|
||||||
@@ -17,7 +19,7 @@ import {
|
|||||||
} from "../../actions/commonActions";
|
} from "../../actions/commonActions";
|
||||||
import { registerDevice } from "../../actions/deviceActions";
|
import { registerDevice } from "../../actions/deviceActions";
|
||||||
import { getWelcomeEventInfo } from "../../actions/eventActions";
|
import { getWelcomeEventInfo } from "../../actions/eventActions";
|
||||||
import { fetchCurrentUserHomeTerms } from "../../actions/homeActions";
|
// import { fetchCurrentUserHomeTermsPromise } from "../../actions/homeActions";
|
||||||
import {
|
import {
|
||||||
sendLogGNB,
|
sendLogGNB,
|
||||||
sendLogTerms,
|
sendLogTerms,
|
||||||
@@ -29,27 +31,29 @@ import TButton, { TYPES } from "../../components/TButton/TButton";
|
|||||||
// import TButtonTab from "../../components/TButtonTab/TButtonTab";
|
// import TButtonTab from "../../components/TButtonTab/TButtonTab";
|
||||||
import TCheckBoxSquare from "../../components/TCheckBox/TCheckBoxSquare";
|
import TCheckBoxSquare from "../../components/TCheckBox/TCheckBoxSquare";
|
||||||
import TPanel from "../../components/TPanel/TPanel";
|
import TPanel from "../../components/TPanel/TPanel";
|
||||||
import TPopUp, { CONTENT_TYPES } from "../../components/TPopUp/TPopUp";
|
import TPopUp from "../../components/TPopUp/TPopUp";
|
||||||
import TNewPopUp from "../../components/TPopUp/TNewPopUp";
|
import TNewPopUp from "../../components/TPopUp/TNewPopUp";
|
||||||
import OptionalTermsInfo from "../MyPagePanel/MyPageSub/TermsOfService/OptionalTermsInfo";
|
// import OptionalTermsInfo from "../MyPagePanel/MyPageSub/TermsOfService/OptionalTermsInfo";
|
||||||
import useDebugKey from "../../hooks/useDebugKey";
|
import useDebugKey from "../../hooks/useDebugKey";
|
||||||
|
import useSafeFocusState from "../../hooks/useSafeFocusState";
|
||||||
|
import useTermsStateMachine from "../../hooks/useTermsStateMachine";
|
||||||
|
import useRightPanelContent from "../../hooks/useRightPanelContent";
|
||||||
import * as Config from "../../utils/Config";
|
import * as Config from "../../utils/Config";
|
||||||
import { panel_names } from "../../utils/Config";
|
import { panel_names } from "../../utils/Config";
|
||||||
import { $L, scaleH, scaleW } from "../../utils/helperMethods";
|
import { $L } from "../../utils/helperMethods";
|
||||||
import css from "./IntroPanel.new.module.less";
|
import css from "./IntroPanel.new.module.less";
|
||||||
import { types } from "../../actions/actionTypes";
|
import { types } from "../../actions/actionTypes";
|
||||||
import { focusById } from "../../utils/spotlight-utils";
|
import { focusById } from "../../utils/spotlight-utils";
|
||||||
|
|
||||||
|
|
||||||
const Container = SpotlightContainerDecorator(
|
const Container = SpotlightContainerDecorator(
|
||||||
{ enterTo: "last-focused" },
|
{ enterTo: "last-focused" },
|
||||||
"div"
|
"div",
|
||||||
);
|
);
|
||||||
|
|
||||||
export default function IntroPanel({
|
export default function IntroPanel({
|
||||||
children,
|
// children,
|
||||||
isTabActivated,
|
// isTabActivated,
|
||||||
handleCancel,
|
// handleCancel,
|
||||||
spotlightId,
|
spotlightId,
|
||||||
...rest
|
...rest
|
||||||
}) {
|
}) {
|
||||||
@@ -58,36 +62,50 @@ export default function IntroPanel({
|
|||||||
|
|
||||||
useDebugKey({});
|
useDebugKey({});
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const blurTimeout = useRef(null);
|
// const blurTimeout = useRef(null);
|
||||||
const termsData = useSelector((state) => state.home.termsData);
|
const termsData = useSelector((state) => state.home.termsData);
|
||||||
const { popupVisible, activePopup, ...popupState } = useSelector(
|
const { popupVisible, activePopup, ...popupState } = useSelector(
|
||||||
(state) => state.common.popup
|
(state) => state.common.popup,
|
||||||
);
|
);
|
||||||
const eventInfos = useSelector((state) => state.event.eventData);
|
// const eventInfos = useSelector((state) => state.event.eventData);
|
||||||
const regDeviceData = useSelector((state) => state.device.regDeviceData);
|
const regDeviceData = useSelector((state) => state.device.regDeviceData);
|
||||||
const regDeviceInfoData = useSelector(
|
// const regDeviceInfoData = useSelector(
|
||||||
(state) => state.device.regDeviceInfoData
|
// (state) => state.device.regDeviceInfoData
|
||||||
);
|
// );
|
||||||
|
|
||||||
// registerDevice API 호출 중 여부
|
// registerDevice API 호출 중 여부
|
||||||
const [isProcessing, setIsProcessing] = useState(false);
|
const [isProcessing, setIsProcessing] = useState(false);
|
||||||
const [showExitMessagePopup, setShowExitMessagePopup] = useState(false);
|
const [showExitMessagePopup, setShowExitMessagePopup] = useState(false);
|
||||||
const [isRequiredFocused, setIsRequiredFocused] = useState(false);
|
// const [isRequiredFocused, setIsRequiredFocused] = useState(false);
|
||||||
|
|
||||||
|
const { focusedItem, setFocusAsync, clearFocusAsync } = useSafeFocusState();
|
||||||
|
const { state: termsState, updateStateAsync } = useTermsStateMachine();
|
||||||
|
const {
|
||||||
|
termsChecked,
|
||||||
|
privacyChecked,
|
||||||
|
optionalChecked,
|
||||||
|
selectAllChecked,
|
||||||
|
error: termsError, // 훅의 에러 상태를 가져옴
|
||||||
|
} = termsState;
|
||||||
|
|
||||||
const introTermsData = useMemo(() => {
|
const introTermsData = useMemo(() => {
|
||||||
return termsData?.data?.terms.filter(
|
return (
|
||||||
(item) => item.trmsTpCd === "MST00401" || item.trmsTpCd === "MST00402"
|
termsData?.data?.terms.filter(
|
||||||
) || [];
|
(item) => item.trmsTpCd === "MST00401" || item.trmsTpCd === "MST00402",
|
||||||
|
) || []
|
||||||
|
);
|
||||||
}, [termsData]);
|
}, [termsData]);
|
||||||
|
|
||||||
const optionalTermsData = useMemo(() => {
|
const optionalTermsData = useMemo(() => {
|
||||||
return termsData?.data?.terms.filter(
|
return (
|
||||||
(item) => item.trmsTpCd === "MST00405"
|
termsData?.data?.terms.filter(
|
||||||
) || [];
|
(item) => item.trmsTpCd === "MST00405" || item.trmsTpCd === "MST00406",
|
||||||
|
) || []
|
||||||
|
);
|
||||||
}, [termsData]);
|
}, [termsData]);
|
||||||
|
|
||||||
const webOSVersion = useSelector(
|
const webOSVersion = useSelector(
|
||||||
(state) => state.common.appStatus?.webOSVersion
|
(state) => state.common.appStatus?.webOSVersion,
|
||||||
);
|
);
|
||||||
// WebOS 버전별 UI 표시 모드 결정
|
// WebOS 버전별 UI 표시 모드 결정
|
||||||
// 이미지 표시: 4.0, 5.0, 23, 24
|
// 이미지 표시: 4.0, 5.0, 23, 24
|
||||||
@@ -98,30 +116,27 @@ export default function IntroPanel({
|
|||||||
const version = String(webOSVersion);
|
const version = String(webOSVersion);
|
||||||
|
|
||||||
// 텍스트 표시 버전들
|
// 텍스트 표시 버전들
|
||||||
const textVersions = ['4.5', '6.0', '22'];
|
const textVersions = ["4.5", "6.0", "22"];
|
||||||
|
|
||||||
// 이미지 표시 버전들
|
// 이미지 표시 버전들
|
||||||
const imageVersions = ['4.0', '5.0', '23', '24'];
|
const imageVersions = ["4.0", "5.0", "23", "24"];
|
||||||
|
|
||||||
// 텍스트 버전인지 확인
|
// 텍스트 버전인지 확인
|
||||||
const shouldShowText = textVersions.includes(version);
|
const shouldShowText = textVersions.includes(version);
|
||||||
|
|
||||||
console.log('🔍 WebOS 버전별 UI 모드:');
|
if (process.env.NODE_ENV === "development") {
|
||||||
console.log(' - webOSVersion:', version);
|
console.log("🔍 WebOS 버전별 UI 모드:");
|
||||||
console.log(' - shouldShowText (텍스트 모드):', shouldShowText);
|
console.log(" - webOSVersion:", version);
|
||||||
console.log(' - 텍스트 버전들:', textVersions);
|
console.log(" - shouldShowText (텍스트 모드):", shouldShowText);
|
||||||
console.log(' - 이미지 버전들:', imageVersions);
|
console.log(" - 텍스트 버전들:", textVersions);
|
||||||
|
console.log(" - 이미지 버전들:", imageVersions);
|
||||||
|
}
|
||||||
|
|
||||||
return shouldShowText;
|
return shouldShowText;
|
||||||
}, [webOSVersion]);
|
}, [webOSVersion]);
|
||||||
|
|
||||||
// 상태 관리
|
// 상태 관리
|
||||||
const [currentTerms, setCurrentTerms] = useState(null);
|
const [currentTerms, setCurrentTerms] = useState(null);
|
||||||
const [termsChecked, setTermsChecked] = useState(false); // Terms & Conditions 기본 체크
|
|
||||||
const [privacyChecked, setPrivacyChecked] = useState(false); // Privacy Policy 기본 체크
|
|
||||||
const [optionalChecked, setOptionalChecked] = useState(false); // Optional Terms 기본 체크 안됨
|
|
||||||
const [selectAllChecked, setSelectAllChecked] = useState(false);
|
|
||||||
const [focusedItem, setFocusedItem] = useState(null); // 포커스 추적 상태 추가
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
dispatch({ type: types.REGISTER_DEVICE_RESET });
|
dispatch({ type: types.REGISTER_DEVICE_RESET });
|
||||||
@@ -139,9 +154,11 @@ export default function IntroPanel({
|
|||||||
|
|
||||||
// 디버깅용 WebOS 버전 로그
|
// 디버깅용 WebOS 버전 로그
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log('🔍 IntroPanel WebOS 버전 정보:');
|
if (process.env.NODE_ENV === "development") {
|
||||||
console.log(' - webOSVersion:', webOSVersion);
|
console.log("🔍 IntroPanel WebOS 버전 정보:");
|
||||||
console.log(' - shouldShowBenefitsView:', shouldShowBenefitsView);
|
console.log(" - webOSVersion:", webOSVersion);
|
||||||
|
console.log(" - shouldShowBenefitsView:", shouldShowBenefitsView);
|
||||||
|
}
|
||||||
}, [webOSVersion, shouldShowBenefitsView]);
|
}, [webOSVersion, shouldShowBenefitsView]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -153,22 +170,13 @@ export default function IntroPanel({
|
|||||||
contextName: Config.LOG_CONTEXT_NAME.SHOPTIME,
|
contextName: Config.LOG_CONTEXT_NAME.SHOPTIME,
|
||||||
messageId: Config.LOG_MESSAGE_ID.VIEW_CHANGE,
|
messageId: Config.LOG_MESSAGE_ID.VIEW_CHANGE,
|
||||||
visible: false,
|
visible: false,
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
}, 3000);
|
}, 3000);
|
||||||
return () => clearTimeout(timer);
|
return () => clearTimeout(timer);
|
||||||
}
|
}
|
||||||
}, [showExitMessagePopup, dispatch]);
|
}, [showExitMessagePopup, dispatch]);
|
||||||
|
|
||||||
// Select All 상태 업데이트
|
|
||||||
useEffect(() => {
|
|
||||||
const allChecked = termsChecked && privacyChecked && optionalChecked;
|
|
||||||
setSelectAllChecked(allChecked);
|
|
||||||
if (allChecked) {
|
|
||||||
Spotlight.focus("agreeButton");
|
|
||||||
}
|
|
||||||
}, [termsChecked, privacyChecked, optionalChecked]);
|
|
||||||
|
|
||||||
// 컴포넌트 마운트 후 0.5초 뒤에 selectAllCheckbox으로 강제 포커스
|
// 컴포넌트 마운트 후 0.5초 뒤에 selectAllCheckbox으로 강제 포커스
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const focusTimer = setTimeout(() => {
|
const focusTimer = setTimeout(() => {
|
||||||
@@ -180,6 +188,17 @@ export default function IntroPanel({
|
|||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
// [추가] useTermsStateMachine의 에러 상태를 감지하여 팝업으로 표시
|
||||||
|
useEffect(() => {
|
||||||
|
if (termsError) {
|
||||||
|
dispatch(setShowPopup(Config.ACTIVE_POPUP.alertPopup, {
|
||||||
|
title: $L("Error"),
|
||||||
|
text: termsError.message,
|
||||||
|
button1Text: $L("OK")
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}, [termsError, dispatch]);
|
||||||
|
|
||||||
// 약관 팝업 동의여부에 따른 이벤트 핸들러
|
// 약관 팝업 동의여부에 따른 이벤트 핸들러
|
||||||
const handleTermsAgree = useCallback(() => {
|
const handleTermsAgree = useCallback(() => {
|
||||||
if (!currentTerms) {
|
if (!currentTerms) {
|
||||||
@@ -187,23 +206,22 @@ export default function IntroPanel({
|
|||||||
}
|
}
|
||||||
const termType = currentTerms.trmsTpCd;
|
const termType = currentTerms.trmsTpCd;
|
||||||
if (termType === "MST00402") {
|
if (termType === "MST00402") {
|
||||||
setTermsChecked(true);
|
updateStateAsync({ termsChecked: true });
|
||||||
} else if (termType === "MST00401") {
|
} else if (termType === "MST00401") {
|
||||||
setPrivacyChecked(true);
|
updateStateAsync({ privacyChecked: true });
|
||||||
} else if (termType === "MST00405") {
|
} else if (termType === "MST00405") {
|
||||||
// Optional Terms
|
// Optional Terms
|
||||||
setOptionalChecked(true);
|
updateStateAsync({ optionalChecked: true });
|
||||||
}
|
}
|
||||||
// 팝업 닫기
|
// 팝업 닫기
|
||||||
dispatch(setHidePopup());
|
dispatch(setHidePopup());
|
||||||
}, [currentTerms, dispatch]);
|
}, [currentTerms, dispatch, updateStateAsync]);
|
||||||
|
|
||||||
|
|
||||||
const handleTermsClick = useCallback(
|
const handleTermsClick = useCallback(
|
||||||
(trmsTpCdList) => {
|
(trmsTpCdList) => {
|
||||||
if (introTermsData) {
|
if (introTermsData) {
|
||||||
const selectedTerms = introTermsData.find(
|
const selectedTerms = introTermsData.find(
|
||||||
(term) => term.trmsTpCd === trmsTpCdList
|
(term) => term.trmsTpCd === trmsTpCdList,
|
||||||
);
|
);
|
||||||
|
|
||||||
setCurrentTerms(selectedTerms);
|
setCurrentTerms(selectedTerms);
|
||||||
@@ -216,14 +234,14 @@ export default function IntroPanel({
|
|||||||
dispatch(sendLogTerms({ logTpNo }));
|
dispatch(sendLogTerms({ logTpNo }));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[introTermsData, dispatch]
|
[introTermsData, dispatch],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleOptionalTermsClick = useCallback(
|
const handleOptionalTermsClick = useCallback(
|
||||||
(trmsTpCdList) => {
|
(trmsTpCdList) => {
|
||||||
if (optionalTermsData) {
|
if (optionalTermsData) {
|
||||||
const selectedTerms = optionalTermsData.find(
|
const selectedTerms = optionalTermsData.find(
|
||||||
(term) => term.trmsTpCd === trmsTpCdList
|
(term) => term.trmsTpCd === trmsTpCdList,
|
||||||
);
|
);
|
||||||
|
|
||||||
setCurrentTerms(selectedTerms);
|
setCurrentTerms(selectedTerms);
|
||||||
@@ -236,10 +254,9 @@ export default function IntroPanel({
|
|||||||
// dispatch(sendLogTerms({ logTpNo }));
|
// dispatch(sendLogTerms({ logTpNo }));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[optionalTermsData, dispatch]
|
[optionalTermsData, dispatch],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
const onClose = useCallback(() => {
|
const onClose = useCallback(() => {
|
||||||
dispatch(setHidePopup());
|
dispatch(setHidePopup());
|
||||||
}, [dispatch]);
|
}, [dispatch]);
|
||||||
@@ -260,7 +277,7 @@ export default function IntroPanel({
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
setIsProcessing(true);
|
setIsProcessing(true);
|
||||||
// 약관 동의 처리 시작 시 로딩 상태로 설정
|
// 약관 동의 처리 시작 시 로딩 상태로 설정
|
||||||
dispatch({ type: types.GET_TERMS_AGREE_YN_START });
|
dispatch({ type: types.GET_TERMS_AGREE_YN_START });
|
||||||
|
|
||||||
// 약관 ID 정확하게 매핑
|
// 약관 ID 정확하게 매핑
|
||||||
@@ -276,13 +293,15 @@ export default function IntroPanel({
|
|||||||
agreeTerms.push("TID0000232"); // MST00405 -> TID0000232 (선택약관)
|
agreeTerms.push("TID0000232"); // MST00405 -> TID0000232 (선택약관)
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('최종 전송될 agreeTerms:', agreeTerms);
|
if (process.env.NODE_ENV === "development") {
|
||||||
|
console.log("최종 전송될 agreeTerms:", agreeTerms);
|
||||||
|
}
|
||||||
|
|
||||||
dispatch(
|
dispatch(
|
||||||
registerDevice(
|
registerDevice(
|
||||||
{ agreeTerms: agreeTerms },
|
{ agreeTerms: agreeTerms },
|
||||||
(regDeviceData) => {
|
(newRegDeviceData) => {
|
||||||
if (regDeviceData && regDeviceData.retCode === 0) {
|
if (newRegDeviceData && newRegDeviceData.retCode === 0) {
|
||||||
dispatch(
|
dispatch(
|
||||||
getWelcomeEventInfo((eventInfos) => {
|
getWelcomeEventInfo((eventInfos) => {
|
||||||
if (
|
if (
|
||||||
@@ -301,7 +320,7 @@ export default function IntroPanel({
|
|||||||
}
|
}
|
||||||
|
|
||||||
dispatch(
|
dispatch(
|
||||||
sendLogTerms({ logTpNo: Config.LOG_TP_NO.TERMS.AGREE })
|
sendLogTerms({ logTpNo: Config.LOG_TP_NO.TERMS.AGREE }),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (displayWelcomeEventPanel) {
|
if (displayWelcomeEventPanel) {
|
||||||
@@ -309,13 +328,13 @@ export default function IntroPanel({
|
|||||||
pushPanel({
|
pushPanel({
|
||||||
name: panel_names.WELCOME_EVENT_PANEL,
|
name: panel_names.WELCOME_EVENT_PANEL,
|
||||||
panelInfo: { eventInfos: eventInfos.data },
|
panelInfo: { eventInfos: eventInfos.data },
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dispatch(popPanel(panel_names.INTRO_PANEL));
|
dispatch(popPanel(panel_names.INTRO_PANEL));
|
||||||
setIsProcessing(false);
|
setIsProcessing(false);
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
dispatch(
|
dispatch(
|
||||||
@@ -323,7 +342,7 @@ export default function IntroPanel({
|
|||||||
title: $L("Error"),
|
title: $L("Error"),
|
||||||
text: $L("Device registration failed. Please try again."),
|
text: $L("Device registration failed. Please try again."),
|
||||||
button1Text: $L("OK"),
|
button1Text: $L("OK"),
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
setIsProcessing(false);
|
setIsProcessing(false);
|
||||||
}
|
}
|
||||||
@@ -334,11 +353,11 @@ export default function IntroPanel({
|
|||||||
title: $L("Error"),
|
title: $L("Error"),
|
||||||
text: $L("Device registration failed. Please try again."),
|
text: $L("Device registration failed. Please try again."),
|
||||||
button1Text: $L("OK"),
|
button1Text: $L("OK"),
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
setIsProcessing(false);
|
setIsProcessing(false);
|
||||||
}
|
},
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
}, [
|
}, [
|
||||||
termsChecked,
|
termsChecked,
|
||||||
@@ -353,16 +372,18 @@ export default function IntroPanel({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// isProcessing이 true일 때만 실패 체크 (= handleAgree 클릭 후에만)
|
// isProcessing이 true일 때만 실패 체크 (= handleAgree 클릭 후에만)
|
||||||
if (isProcessing && regDeviceData && regDeviceData.retCode !== 0) {
|
if (isProcessing && regDeviceData && regDeviceData.retCode !== 0) {
|
||||||
console.error(
|
if (process.env.NODE_ENV === "development") {
|
||||||
`[IntroPanel] registerDevice 실패: isProcessing=${isProcessing}, retCode=${regDeviceData.retCode}`,
|
console.error(
|
||||||
regDeviceData
|
`[IntroPanel] registerDevice 실패: isProcessing=${isProcessing}, retCode=${regDeviceData.retCode}`,
|
||||||
);
|
regDeviceData,
|
||||||
|
);
|
||||||
|
}
|
||||||
dispatch(
|
dispatch(
|
||||||
setShowPopup(Config.ACTIVE_POPUP.alertPopup, {
|
setShowPopup(Config.ACTIVE_POPUP.alertPopup, {
|
||||||
title: $L("Error"),
|
title: $L("Error"),
|
||||||
text: $L("Device registration failed. Please try again."),
|
text: $L("Device registration failed. Please try again."),
|
||||||
button1Text: $L("OK"),
|
button1Text: $L("OK"),
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
setIsProcessing(false);
|
setIsProcessing(false);
|
||||||
}
|
}
|
||||||
@@ -382,7 +403,7 @@ export default function IntroPanel({
|
|||||||
contextName: Config.LOG_CONTEXT_NAME.SHOPTIME,
|
contextName: Config.LOG_CONTEXT_NAME.SHOPTIME,
|
||||||
messageId: Config.LOG_MESSAGE_ID.VIEW_CHANGE,
|
messageId: Config.LOG_MESSAGE_ID.VIEW_CHANGE,
|
||||||
visible: false,
|
visible: false,
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
}, [dispatch]);
|
}, [dispatch]);
|
||||||
|
|
||||||
@@ -392,101 +413,138 @@ export default function IntroPanel({
|
|||||||
}
|
}
|
||||||
}, [dispatch, activePopup]);
|
}, [dispatch, activePopup]);
|
||||||
|
|
||||||
const handleFocus = useCallback((item) => {
|
const handleFocus = useCallback(
|
||||||
if (blurTimeout.current) {
|
(item) => {
|
||||||
clearTimeout(blurTimeout.current);
|
setFocusAsync(item);
|
||||||
blurTimeout.current = null;
|
},
|
||||||
}
|
[setFocusAsync]
|
||||||
setFocusedItem(item);
|
);
|
||||||
}, []);
|
|
||||||
|
|
||||||
const handleBlur = useCallback(() => {
|
const handleBlur = useCallback(() => {
|
||||||
blurTimeout.current = setTimeout(() => {
|
clearFocusAsync(0);
|
||||||
setFocusedItem(null);
|
}, [clearFocusAsync]);
|
||||||
}, 0);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// 체크박스 핸들러들
|
// 체크박스 핸들러들
|
||||||
const handleTermsToggle = useCallback(({ selected }) => {
|
const handleTermsToggle = useCallback(
|
||||||
setTermsChecked(selected);
|
async ({ selected }) => {
|
||||||
}, []);
|
try {
|
||||||
|
const newState = await updateStateAsync({ termsChecked: selected });
|
||||||
|
if (newState.termsChecked && newState.privacyChecked && newState.optionalChecked) {
|
||||||
|
setTimeout(() => Spotlight.focus("agreeButton"), 100);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
if (process.env.NODE_ENV === "development") {
|
||||||
|
console.error("Toggle failed:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[updateStateAsync]
|
||||||
|
);
|
||||||
|
|
||||||
const handlePrivacyToggle = useCallback(({ selected }) => {
|
const handlePrivacyToggle = useCallback(
|
||||||
setPrivacyChecked(selected);
|
async ({ selected }) => {
|
||||||
}, []);
|
try {
|
||||||
|
const newState = await updateStateAsync({ privacyChecked: selected });
|
||||||
|
if (newState.termsChecked && newState.privacyChecked && newState.optionalChecked) {
|
||||||
|
setTimeout(() => Spotlight.focus("agreeButton"), 100);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
if (process.env.NODE_ENV === "development") {
|
||||||
|
console.error("Toggle failed:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[updateStateAsync]
|
||||||
|
);
|
||||||
|
|
||||||
const handleOptionalToggle = useCallback(({ selected }) => {
|
const handleOptionalToggle = useCallback(
|
||||||
setOptionalChecked(selected);
|
async ({ selected }) => {
|
||||||
}, []);
|
try {
|
||||||
|
const newState = await updateStateAsync({ optionalChecked: selected });
|
||||||
|
if (newState.termsChecked && newState.privacyChecked && newState.optionalChecked) {
|
||||||
|
setTimeout(() => Spotlight.focus("agreeButton"), 100);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
if (process.env.NODE_ENV === "development") {
|
||||||
|
console.error("Toggle failed:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[updateStateAsync]
|
||||||
|
);
|
||||||
|
|
||||||
const handleSelectAllToggle = useCallback(({ selected }) => {
|
const handleSelectAllToggle = useCallback(
|
||||||
setSelectAllChecked(selected);
|
async ({ selected }) => {
|
||||||
setTermsChecked(selected);
|
try {
|
||||||
setPrivacyChecked(selected);
|
const newState = await updateStateAsync({ selectAllChecked: selected });
|
||||||
setOptionalChecked(selected);
|
if (newState.selectAllChecked) {
|
||||||
}, []);
|
setTimeout(() => Spotlight.focus("agreeButton"), 100);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
if (process.env.NODE_ENV === "development") {
|
||||||
|
console.error("Toggle failed:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[updateStateAsync]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleTermsClickMST00402 = useCallback(
|
||||||
|
() => handleTermsClick("MST00402"),
|
||||||
|
[handleTermsClick],
|
||||||
|
);
|
||||||
|
const handleTermsClickMST00401 = useCallback(
|
||||||
|
() => handleTermsClick("MST00401"),
|
||||||
|
[handleTermsClick],
|
||||||
|
);
|
||||||
|
const handleOptionalTermsClickMST00405 = useCallback(
|
||||||
|
() => handleOptionalTermsClick("MST00405"),
|
||||||
|
[handleOptionalTermsClick],
|
||||||
|
);
|
||||||
|
|
||||||
const rightPanelContent = useMemo(() => {
|
const handleFocusTermsCheckbox = useCallback(
|
||||||
const requiredItemIds = [
|
() => handleFocus("termsCheckbox"),
|
||||||
"termsCheckbox",
|
[handleFocus],
|
||||||
"termsButton",
|
);
|
||||||
"privacyCheckbox",
|
const handleFocusTermsButton = useCallback(
|
||||||
"privacyButton",
|
() => handleFocus("termsButton"),
|
||||||
];
|
[handleFocus],
|
||||||
if (!requiredItemIds.includes(focusedItem)) {
|
);
|
||||||
return (
|
const handleFocusPrivacyCheckbox = useCallback(
|
||||||
<>
|
() => handleFocus("privacyCheckbox"),
|
||||||
{shouldShowBenefitsView ? (
|
[handleFocus],
|
||||||
<div className={css.optionalDescription}>
|
);
|
||||||
{$L(
|
const handleFocusPrivacyButton = useCallback(
|
||||||
'By checking "Optional terms", you allow Shop Time to use your activity (views, purchases, searches, etc.) to show you more relevant content, product recommendations, special offers, and ads. If you do not check, you can still use all basic Shop Time features'
|
() => handleFocus("privacyButton"),
|
||||||
)}
|
[handleFocus],
|
||||||
</div>
|
);
|
||||||
) : (
|
const handleFocusOptionalCheckbox = useCallback(
|
||||||
<OptionalTermsInfo
|
() => handleFocus("optionalCheckbox"),
|
||||||
displayMode="image"
|
[handleFocus],
|
||||||
imageTitle={$L("Agree and Enjoy Special Benefits")}
|
);
|
||||||
spotlightId="optional-terms-info"
|
const handleFocusOptionalButton = useCallback(
|
||||||
/>
|
() => handleFocus("optionalButton"),
|
||||||
)}
|
[handleFocus],
|
||||||
</>
|
);
|
||||||
);
|
const handleFocusSelectAllCheckbox = useCallback(
|
||||||
}
|
() => handleFocus("selectAllCheckbox"),
|
||||||
|
[handleFocus],
|
||||||
const hasRequiredUnchecked = !termsChecked || !privacyChecked;
|
);
|
||||||
|
const handleFocusAgreeButton = useCallback(
|
||||||
// 우선순위 1: 필수 약관이 체크 안됨 → 항상 경고 메시지
|
() => handleFocus("agreeButton"),
|
||||||
if (hasRequiredUnchecked) {
|
[handleFocus],
|
||||||
return (
|
);
|
||||||
<div className={css.requiredInfoPanel}>
|
const handleFocusDisagreeButton = useCallback(
|
||||||
<p className={css.infoText}>{$L("Required Consent")}</p>
|
() => handleFocus("disagreeButton"),
|
||||||
<p className={css.infoText}>
|
[handleFocus],
|
||||||
{$L("(Necessary for using the service)")}
|
);
|
||||||
</p>
|
|
||||||
<div className={css.warningContainer}>
|
|
||||||
<p className={css.warningText}>
|
|
||||||
{$L("Please agree to the required Terms & Conditions and")}
|
|
||||||
</p>
|
|
||||||
<p className={css.warningText}>
|
|
||||||
{$L("Privacy Policy to start enjoying Shop Time")}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 우선순위 2: 필수 약관에 포커스 있음 → 필수 약관 안내
|
|
||||||
return (
|
|
||||||
<div className={css.requiredInfoPanel}>
|
|
||||||
<p className={css.infoText}>{$L("Required Consent")}</p>
|
|
||||||
<p className={css.infoText}>
|
|
||||||
{$L("(Necessary for using the service)")}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}, [termsChecked, privacyChecked, focusedItem, shouldShowBenefitsView]);
|
|
||||||
|
|
||||||
|
const rightPanelContent = useRightPanelContent(
|
||||||
|
focusedItem,
|
||||||
|
termsChecked,
|
||||||
|
privacyChecked,
|
||||||
|
shouldShowBenefitsView
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
Spotlight.focus();
|
Spotlight.focus();
|
||||||
@@ -500,19 +558,23 @@ export default function IntroPanel({
|
|||||||
// }, [dispatch, regDeviceInfoData]);
|
// }, [dispatch, regDeviceInfoData]);
|
||||||
|
|
||||||
const description = $L(
|
const description = $L(
|
||||||
"Check out more LIVE SHOWS now and enjoy shopping at Shop Time's special price on your TV by agreeing to the LG TV Shopping Terms and Conditions."
|
"Check out more LIVE SHOWS now and enjoy shopping at Shop Time's special price on your TV by agreeing to the LG TV Shopping Terms and Conditions.",
|
||||||
);
|
);
|
||||||
const title = "welcome to shoptime!";
|
const title = "welcome to shoptime!";
|
||||||
delete rest.isOnTop;
|
delete rest.isOnTop;
|
||||||
|
|
||||||
// [추가] 약관 종류에 따라 팝업 제목을 반환하는 헬퍼 함수
|
// [추가] 약관 종류에 따라 팝업 제목을 반환하는 헬퍼 함수
|
||||||
const getTermsPopupTitle = (terms) => {
|
const getTermsPopupTitle = (terms) => {
|
||||||
if (!terms) return '';
|
if (!terms) return "";
|
||||||
switch (terms.trmsTpCd) {
|
switch (terms.trmsTpCd) {
|
||||||
case "MST00401": return $L("Privacy Policy");
|
case "MST00401":
|
||||||
case "MST00402": return $L("Terms & Conditions");
|
return $L("Privacy Policy");
|
||||||
case "MST00405": return $L("Optional Terms");
|
case "MST00402":
|
||||||
default: return '';
|
return $L("Terms & Conditions");
|
||||||
|
case "MST00405":
|
||||||
|
return $L("Optional Terms");
|
||||||
|
default:
|
||||||
|
return "";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -528,9 +590,7 @@ export default function IntroPanel({
|
|||||||
{/* 첫 번째 영역: 헤더 섹션 */}
|
{/* 첫 번째 영역: 헤더 섹션 */}
|
||||||
<div className={css.headerSection}>
|
<div className={css.headerSection}>
|
||||||
<div className={css.titleContainer}>
|
<div className={css.titleContainer}>
|
||||||
<div className={css.welcomeText}>
|
<div className={css.welcomeText}>{$L("Welcome to")}</div>
|
||||||
{$L("Welcome to")}
|
|
||||||
</div>
|
|
||||||
<div className={css.brandContainer}>
|
<div className={css.brandContainer}>
|
||||||
<span className={css.shopText}>{$L("Sh")}</span>
|
<span className={css.shopText}>{$L("Sh")}</span>
|
||||||
<span className={css.oText}>{$L("o")}</span>
|
<span className={css.oText}>{$L("o")}</span>
|
||||||
@@ -538,9 +598,7 @@ export default function IntroPanel({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={css.descriptionContainer}>
|
<div className={css.descriptionContainer}>
|
||||||
<div className={css.descriptionText}>
|
<div className={css.descriptionText}>{description}</div>
|
||||||
{description}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -553,15 +611,15 @@ export default function IntroPanel({
|
|||||||
className={css.customeCheckbox}
|
className={css.customeCheckbox}
|
||||||
selected={termsChecked}
|
selected={termsChecked}
|
||||||
onToggle={handleTermsToggle}
|
onToggle={handleTermsToggle}
|
||||||
onFocus={() => handleFocus('termsCheckbox')}
|
onFocus={handleFocusTermsCheckbox}
|
||||||
onBlur={handleBlur}
|
onBlur={handleBlur}
|
||||||
spotlightId="termsCheckbox"
|
spotlightId="termsCheckbox"
|
||||||
ariaLabel={$L("Terms & Conditions checkbox")}
|
ariaLabel={$L("Terms & Conditions checkbox")}
|
||||||
/>
|
/>
|
||||||
<TButton
|
<TButton
|
||||||
className={css.termsButton}
|
className={css.termsButton}
|
||||||
onClick={() => handleTermsClick("MST00402")}
|
onClick={handleTermsClickMST00402}
|
||||||
onFocus={() => handleFocus('termsButton')}
|
onFocus={handleFocusTermsButton}
|
||||||
onBlur={handleBlur}
|
onBlur={handleBlur}
|
||||||
spotlightId="termsButton"
|
spotlightId="termsButton"
|
||||||
type={TYPES.terms}
|
type={TYPES.terms}
|
||||||
@@ -579,15 +637,15 @@ export default function IntroPanel({
|
|||||||
className={css.customeCheckbox}
|
className={css.customeCheckbox}
|
||||||
selected={privacyChecked}
|
selected={privacyChecked}
|
||||||
onToggle={handlePrivacyToggle}
|
onToggle={handlePrivacyToggle}
|
||||||
onFocus={() => handleFocus('privacyCheckbox')}
|
onFocus={handleFocusPrivacyCheckbox}
|
||||||
onBlur={handleBlur}
|
onBlur={handleBlur}
|
||||||
spotlightId="privacyCheckbox"
|
spotlightId="privacyCheckbox"
|
||||||
ariaLabel={$L("Privacy Policy checkbox")}
|
ariaLabel={$L("Privacy Policy checkbox")}
|
||||||
/>
|
/>
|
||||||
<TButton
|
<TButton
|
||||||
className={css.termsButton}
|
className={css.termsButton}
|
||||||
onClick={() => handleTermsClick("MST00401")}
|
onClick={handleTermsClickMST00401}
|
||||||
onFocus={() => handleFocus('privacyButton')}
|
onFocus={handleFocusPrivacyButton}
|
||||||
onBlur={handleBlur}
|
onBlur={handleBlur}
|
||||||
spotlightId="privacyButton"
|
spotlightId="privacyButton"
|
||||||
type={TYPES.terms}
|
type={TYPES.terms}
|
||||||
@@ -605,29 +663,25 @@ export default function IntroPanel({
|
|||||||
className={css.customeCheckbox}
|
className={css.customeCheckbox}
|
||||||
selected={optionalChecked}
|
selected={optionalChecked}
|
||||||
onToggle={handleOptionalToggle}
|
onToggle={handleOptionalToggle}
|
||||||
onFocus={() => handleFocus('optionalCheckbox')}
|
onFocus={handleFocusOptionalCheckbox}
|
||||||
onBlur={handleBlur}
|
onBlur={handleBlur}
|
||||||
spotlightId="optionalCheckbox"
|
spotlightId="optionalCheckbox"
|
||||||
ariaLabel={$L("Optional Terms checkbox")}
|
ariaLabel={$L("Optional Terms checkbox")}
|
||||||
/>
|
/>
|
||||||
<TButton
|
<TButton
|
||||||
className={css.termsButton}
|
className={css.termsButton}
|
||||||
onClick={() => handleOptionalTermsClick("MST00405")}
|
onClick={handleOptionalTermsClickMST00405}
|
||||||
onFocus={() => handleFocus('optionalButton')}
|
onFocus={handleFocusOptionalButton}
|
||||||
onBlur={handleBlur}
|
onBlur={handleBlur}
|
||||||
spotlightId="optionalButton"
|
spotlightId="optionalButton"
|
||||||
type={TYPES.terms}
|
type={TYPES.terms}
|
||||||
ariaLabel={$L("View Optional Terms")}
|
ariaLabel={$L("View Optional Terms")}
|
||||||
>
|
>
|
||||||
<span className={css.termsText}>
|
<span className={css.termsText}>{$L("Optional Terms")}</span>
|
||||||
{$L("Optional Terms")}
|
|
||||||
</span>
|
|
||||||
</TButton>
|
</TButton>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={css.termsRightPanel}>
|
<div className={css.termsRightPanel}>{rightPanelContent}</div>
|
||||||
{rightPanelContent}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 세 번째 영역: Select All */}
|
{/* 세 번째 영역: Select All */}
|
||||||
@@ -636,14 +690,12 @@ export default function IntroPanel({
|
|||||||
className={css.selectAllCheckbox}
|
className={css.selectAllCheckbox}
|
||||||
selected={selectAllChecked}
|
selected={selectAllChecked}
|
||||||
onToggle={handleSelectAllToggle}
|
onToggle={handleSelectAllToggle}
|
||||||
onFocus={() => handleFocus('selectAllCheckbox')}
|
onFocus={handleFocusSelectAllCheckbox}
|
||||||
onBlur={handleBlur}
|
onBlur={handleBlur}
|
||||||
spotlightId="selectAllCheckbox"
|
spotlightId="selectAllCheckbox"
|
||||||
ariaLabel={$L("Select All checkbox")}
|
ariaLabel={$L("Select All checkbox")}
|
||||||
/>
|
/>
|
||||||
<span className={css.selectAllText}>
|
<span className={css.selectAllText}>{$L("Select All")}</span>
|
||||||
{$L("Select All")}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 네 번째 영역: 버튼 섹션 */}
|
{/* 네 번째 영역: 버튼 섹션 */}
|
||||||
@@ -651,7 +703,7 @@ export default function IntroPanel({
|
|||||||
<TButton
|
<TButton
|
||||||
className={css.agreeButton}
|
className={css.agreeButton}
|
||||||
onClick={handleAgree}
|
onClick={handleAgree}
|
||||||
onFocus={() => handleFocus('agreeButton')}
|
onFocus={handleFocusAgreeButton}
|
||||||
onBlur={handleBlur}
|
onBlur={handleBlur}
|
||||||
spotlightId="agreeButton"
|
spotlightId="agreeButton"
|
||||||
type={TYPES.agree}
|
type={TYPES.agree}
|
||||||
@@ -664,7 +716,7 @@ export default function IntroPanel({
|
|||||||
<TButton
|
<TButton
|
||||||
className={css.disagreeButton}
|
className={css.disagreeButton}
|
||||||
onClick={handleDisagree}
|
onClick={handleDisagree}
|
||||||
onFocus={() => handleFocus('disagreeButton')}
|
onFocus={handleFocusDisagreeButton}
|
||||||
onBlur={handleBlur}
|
onBlur={handleBlur}
|
||||||
spotlightId="disagreeButton"
|
spotlightId="disagreeButton"
|
||||||
type={TYPES.agree}
|
type={TYPES.agree}
|
||||||
@@ -701,8 +753,7 @@ export default function IntroPanel({
|
|||||||
open={popupVisible}
|
open={popupVisible}
|
||||||
kind="figmaTermsPopup"
|
kind="figmaTermsPopup"
|
||||||
title={getTermsPopupTitle(currentTerms)}
|
title={getTermsPopupTitle(currentTerms)}
|
||||||
text={currentTerms?.trmsCntt || ''}
|
text={currentTerms?.trmsCntt || ""}
|
||||||
onIntroTermsAgreeClick={handleTermsAgree} // Agree 버튼 핸들러 연결
|
|
||||||
onClose={onClose} // Close 버튼 핸들러 연결
|
onClose={onClose} // Close 버튼 핸들러 연결
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
@@ -745,10 +796,11 @@ export default function IntroPanel({
|
|||||||
open={showExitMessagePopup}
|
open={showExitMessagePopup}
|
||||||
hasText
|
hasText
|
||||||
title={$L("Exit Shop Time")}
|
title={$L("Exit Shop Time")}
|
||||||
text={$L("Thank you for using the Shop Time, and we hope to see you again. The app will close in 3 seconds.")}
|
text={$L(
|
||||||
|
"Thank you for using the Shop Time, and we hope to see you again. The app will close in 3 seconds.",
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Region>
|
</Region>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,13 +2,13 @@
|
|||||||
@import "../../style/utils.module.less";
|
@import "../../style/utils.module.less";
|
||||||
|
|
||||||
@COLOR_GREEN: #207847;
|
@COLOR_GREEN: #207847;
|
||||||
@COLOR_RED: #C91D53;
|
@COLOR_RED: #c91d53;
|
||||||
@COLOR_GRAY: #807F81;
|
@COLOR_GRAY: #807f81;
|
||||||
@COLOR_GRAY02: #CFCFCF;
|
@COLOR_GRAY02: #cfcfcf;
|
||||||
@COLOR_GRAY03: #57585A;
|
@COLOR_GRAY03: #57585a;
|
||||||
@COLOR_GRAY04: #C5C6C9;
|
@COLOR_GRAY04: #c5c6c9;
|
||||||
@COLOR_GRAY05: #F8F8F8;
|
@COLOR_GRAY05: #f8f8f8;
|
||||||
@COLOR_GRAY06: #57585A;
|
@COLOR_GRAY06: #57585a;
|
||||||
|
|
||||||
.panel {
|
.panel {
|
||||||
> section {
|
> section {
|
||||||
@@ -47,9 +47,9 @@
|
|||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
|
|
||||||
.welcomeText {
|
.welcomeText {
|
||||||
color: #807F81;
|
color: #807f81;
|
||||||
font-size: 62px;
|
font-size: 62px;
|
||||||
font-family: 'LG Smart UI';
|
font-family: "LG Smart UI";
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
line-height: 62px;
|
line-height: 62px;
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
@@ -57,27 +57,27 @@
|
|||||||
|
|
||||||
.brandContainer {
|
.brandContainer {
|
||||||
.shopText {
|
.shopText {
|
||||||
color: #57585A;
|
color: #57585a;
|
||||||
font-size: 70px;
|
font-size: 70px;
|
||||||
font-family: 'LG Smart_Korean';
|
font-family: "LG Smart_Korean";
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
line-height: 75px;
|
line-height: 75px;
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
}
|
}
|
||||||
|
|
||||||
.oText {
|
.oText {
|
||||||
color: #C91D53;
|
color: #c91d53;
|
||||||
font-size: 70px;
|
font-size: 70px;
|
||||||
font-family: 'LG Smart_Korean';
|
font-family: "LG Smart_Korean";
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
line-height: 75px;
|
line-height: 75px;
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
}
|
}
|
||||||
|
|
||||||
.timeText {
|
.timeText {
|
||||||
color: #57585A;
|
color: #57585a;
|
||||||
font-size: 70px;
|
font-size: 70px;
|
||||||
font-family: 'LG Smart_Korean';
|
font-family: "LG Smart_Korean";
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
line-height: 75px;
|
line-height: 75px;
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
@@ -95,9 +95,9 @@
|
|||||||
.descriptionText {
|
.descriptionText {
|
||||||
width: 1012.49px;
|
width: 1012.49px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
color: #807F81;
|
color: #807f81;
|
||||||
font-size: 36px;
|
font-size: 36px;
|
||||||
font-family: 'LG Smart UI';
|
font-family: "LG Smart UI";
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
line-height: 43px;
|
line-height: 43px;
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
@@ -126,7 +126,8 @@
|
|||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 20px;
|
gap: 20px;
|
||||||
display: inline-flex; .checkbox {
|
display: inline-flex;
|
||||||
|
.checkbox {
|
||||||
/* TCheckBoxSquare 컴포넌트가 스타일링을 담당 */
|
/* TCheckBoxSquare 컴포넌트가 스타일링을 담당 */
|
||||||
width: 45px;
|
width: 45px;
|
||||||
height: 45px;
|
height: 45px;
|
||||||
@@ -137,9 +138,9 @@
|
|||||||
height: 120px;
|
height: 120px;
|
||||||
padding: 0 50px;
|
padding: 0 50px;
|
||||||
background: @COLOR_WHITE;
|
background: @COLOR_WHITE;
|
||||||
box-shadow: 0px 0px 30px rgba(0, 0, 0, 0.20);
|
box-shadow: 0px 0px 30px rgba(0, 0, 0, 0.2);
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
border: 1px solid #CFCFCF;
|
border: 1px solid #cfcfcf;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -150,7 +151,7 @@
|
|||||||
.termsText {
|
.termsText {
|
||||||
color: black;
|
color: black;
|
||||||
font-size: 35px;
|
font-size: 35px;
|
||||||
font-family: 'LG Smart UI';
|
font-family: "LG Smart UI";
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
line-height: 35px;
|
line-height: 35px;
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
@@ -166,7 +167,7 @@
|
|||||||
&:focus,
|
&:focus,
|
||||||
&:focus-visible,
|
&:focus-visible,
|
||||||
&:hover {
|
&:hover {
|
||||||
outline: 4px #C91D53 solid !important;
|
outline: 4px #c91d53 solid !important;
|
||||||
outline-offset: 2px !important;
|
outline-offset: 2px !important;
|
||||||
transform: translateY(-2px) !important;
|
transform: translateY(-2px) !important;
|
||||||
box-shadow: 0 4px 12px rgba(201, 29, 83, 0.3) !important;
|
box-shadow: 0 4px 12px rgba(201, 29, 83, 0.3) !important;
|
||||||
@@ -177,12 +178,13 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} .termsRightPanel {
|
}
|
||||||
|
.termsRightPanel {
|
||||||
flex: 1 1 0;
|
flex: 1 1 0;
|
||||||
align-self: stretch;
|
align-self: stretch;
|
||||||
padding-left: 60px;
|
padding-left: 60px;
|
||||||
padding-right: 60px;
|
padding-right: 60px;
|
||||||
border-left: 1px #C5C6C9 solid;
|
border-left: 1px #c5c6c9 solid;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -195,7 +197,8 @@
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 15px;
|
gap: 15px;
|
||||||
display: inline-flex; .selectAllCheckbox {
|
display: inline-flex;
|
||||||
|
.selectAllCheckbox {
|
||||||
/* TCheckBoxSquare 컴포넌트가 스타일링을 담당 */
|
/* TCheckBoxSquare 컴포넌트가 스타일링을 담당 */
|
||||||
width: 45px;
|
width: 45px;
|
||||||
height: 45px;
|
height: 45px;
|
||||||
@@ -204,7 +207,7 @@
|
|||||||
.selectAllText {
|
.selectAllText {
|
||||||
color: @COLOR_BLACK;
|
color: @COLOR_BLACK;
|
||||||
font-size: 35px;
|
font-size: 35px;
|
||||||
font-family: 'LG Smart UI';
|
font-family: "LG Smart UI";
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
line-height: 35px;
|
line-height: 35px;
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
@@ -282,27 +285,30 @@
|
|||||||
|
|
||||||
// 기본 상자 스타일
|
// 기본 상자 스타일
|
||||||
&:before {
|
&:before {
|
||||||
content: '';
|
content: "";
|
||||||
width: 42px;
|
width: 42px;
|
||||||
height: 42px;
|
height: 42px;
|
||||||
background-color: @COLOR_WHITE;
|
background-color: @COLOR_WHITE;
|
||||||
border: 2px solid @COLOR_GRAY02;
|
border: 2px solid @COLOR_GRAY02;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
display: block;
|
display: block;
|
||||||
transition: background-color 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease;
|
transition:
|
||||||
|
background-color 0.2s ease,
|
||||||
|
border-color 0.2s ease,
|
||||||
|
box-shadow 0.2s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ 선택된 상태: 배경색과 테두리 모두 붉은색 + 굵은 테두리
|
// ✅ 선택된 상태: 배경색과 테두리 모두 붉은색 + 굵은 테두리
|
||||||
&.selected:before {
|
&.selected:before {
|
||||||
background-color: #C91D53 !important;
|
background-color: #c91d53 !important;
|
||||||
border: 4px solid #C91D53 !important; // 굵은 테두리로 변경
|
border: 4px solid #c91d53 !important; // 굵은 테두리로 변경
|
||||||
box-shadow: 0 0 8px rgba(201, 29, 83, 0.3); // 약간의 그림자 효과
|
box-shadow: 0 0 8px rgba(201, 29, 83, 0.3); // 약간의 그림자 효과
|
||||||
}
|
}
|
||||||
|
|
||||||
// 포커스 받았지만 선택 안됨
|
// 포커스 받았지만 선택 안됨
|
||||||
&.focused:not(.selected):before {
|
&.focused:not(.selected):before {
|
||||||
background-color: rgba(201, 29, 83, 0.1);
|
background-color: rgba(201, 29, 83, 0.1);
|
||||||
border: 4px solid #C91D53 !important;
|
border: 4px solid #c91d53 !important;
|
||||||
box-shadow: 0 0 10px rgba(199, 8, 80, 0.3) !important;
|
box-shadow: 0 0 10px rgba(199, 8, 80, 0.3) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -315,13 +321,13 @@
|
|||||||
|
|
||||||
// 포커스 받음 (선택된 상태가 아닌 경우에만 적용)
|
// 포커스 받음 (선택된 상태가 아닌 경우에만 적용)
|
||||||
&.focused:not(.selected):before {
|
&.focused:not(.selected):before {
|
||||||
border: 4px solid #C91D53 !important;
|
border: 4px solid #c91d53 !important;
|
||||||
box-shadow: 0 0 10px rgba(199, 8, 80, 0.3) !important;
|
box-shadow: 0 0 10px rgba(199, 8, 80, 0.3) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 체크마크
|
// 체크마크
|
||||||
&.selected:after {
|
&.selected:after {
|
||||||
content: '✓';
|
content: "✓";
|
||||||
color: @COLOR_WHITE;
|
color: @COLOR_WHITE;
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
@@ -356,9 +362,9 @@
|
|||||||
|
|
||||||
.optionalDescription {
|
.optionalDescription {
|
||||||
width: 595px;
|
width: 595px;
|
||||||
color: #807F81;
|
color: #807f81;
|
||||||
font-size: 26px;
|
font-size: 26px;
|
||||||
font-family: 'LG Smart UI';
|
font-family: "LG Smart UI";
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
line-height: 43px;
|
line-height: 43px;
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ import LoadingPanel from "../LoadingPanel/LoadingPanel";
|
|||||||
import MyPagePanel from "../MyPagePanel/MyPagePanel";
|
import MyPagePanel from "../MyPagePanel/MyPagePanel";
|
||||||
import OnSalePanel from "../OnSalePanel/OnSalePanel";
|
import OnSalePanel from "../OnSalePanel/OnSalePanel";
|
||||||
import PlayerPanel from "../PlayerPanel/PlayerPanel";
|
import PlayerPanel from "../PlayerPanel/PlayerPanel";
|
||||||
|
import PlayerPanelNew from "../PlayerPanel/PlayerPanel.new";
|
||||||
import SearchPanel from "../SearchPanel/SearchPanel";
|
import SearchPanel from "../SearchPanel/SearchPanel";
|
||||||
import ThemeCurationPanel from "../ThemeCurationPanel/ThemeCurationPanel";
|
import ThemeCurationPanel from "../ThemeCurationPanel/ThemeCurationPanel";
|
||||||
import TrendingNowPanel from "../TrendingNowPanel/TrendingNowPanel";
|
import TrendingNowPanel from "../TrendingNowPanel/TrendingNowPanel";
|
||||||
@@ -108,6 +109,7 @@ const panelMap = {
|
|||||||
[Config.panel_names.VIDEO_TEST_PANEL]: VideoTestPanel,
|
[Config.panel_names.VIDEO_TEST_PANEL]: VideoTestPanel,
|
||||||
[Config.panel_names.DETAIL_PANEL]: DetailPanel,
|
[Config.panel_names.DETAIL_PANEL]: DetailPanel,
|
||||||
[Config.panel_names.PLAYER_PANEL]: PlayerPanel,
|
[Config.panel_names.PLAYER_PANEL]: PlayerPanel,
|
||||||
|
[Config.panel_names.PLAYER_PANEL_NEW]: PlayerPanelNew,
|
||||||
[Config.panel_names.CHECKOUT_PANEL]: CheckOutPanel,
|
[Config.panel_names.CHECKOUT_PANEL]: CheckOutPanel,
|
||||||
[Config.panel_names.WELCOME_EVENT_PANEL]: WelcomeEventPanel,
|
[Config.panel_names.WELCOME_EVENT_PANEL]: WelcomeEventPanel,
|
||||||
[Config.panel_names.THEME_CURATION_PANEL]: ThemeCurationPanel,
|
[Config.panel_names.THEME_CURATION_PANEL]: ThemeCurationPanel,
|
||||||
@@ -139,7 +141,8 @@ export default function MainView({ className, initService }) {
|
|||||||
const panels = useSelector((state) => state.panels.panels);
|
const panels = useSelector((state) => state.panels.panels);
|
||||||
const lastPanelAction = useSelector((state) => state.panels.lastPanelAction);
|
const lastPanelAction = useSelector((state) => state.panels.lastPanelAction);
|
||||||
const loadingComplete = useSelector((state) => state.common?.loadingComplete);
|
const loadingComplete = useSelector((state) => state.common?.loadingComplete);
|
||||||
const menuData = useSelector((state) => state.home.menuData?.data); const {
|
const menuData = useSelector((state) => state.home.menuData?.data);
|
||||||
|
const {
|
||||||
popupVisible,
|
popupVisible,
|
||||||
activePopup,
|
activePopup,
|
||||||
data: errorCode,
|
data: errorCode,
|
||||||
@@ -154,10 +157,10 @@ export default function MainView({ className, initService }) {
|
|||||||
deviceId,
|
deviceId,
|
||||||
} = useSelector((state) => state.common.appStatus);
|
} = useSelector((state) => state.common.appStatus);
|
||||||
const skipEndOfServicePopup = useSelector(
|
const skipEndOfServicePopup = useSelector(
|
||||||
(state) => state.localSettings.skipEndOfServicePopup
|
(state) => state.localSettings.skipEndOfServicePopup,
|
||||||
);
|
);
|
||||||
const isInternetConnected = useSelector(
|
const isInternetConnected = useSelector(
|
||||||
(state) => state.common.appStatus.isInternetConnected
|
(state) => state.common.appStatus.isInternetConnected,
|
||||||
);
|
);
|
||||||
|
|
||||||
const deviceCountryCode = httpHeader?.["X-Device-Country"] || "";
|
const deviceCountryCode = httpHeader?.["X-Device-Country"] || "";
|
||||||
@@ -172,9 +175,8 @@ export default function MainView({ className, initService }) {
|
|||||||
const [showEndOfServicePopup, setShowEndOfServicePopup] = useState(false);
|
const [showEndOfServicePopup, setShowEndOfServicePopup] = useState(false);
|
||||||
const topPanel = panels[panels.length - 1];
|
const topPanel = panels[panels.length - 1];
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log('🔍 MainView 팝업 상태 변경:', {
|
console.log("🔍 MainView 팝업 상태 변경:", {
|
||||||
popupVisible,
|
popupVisible,
|
||||||
activePopup,
|
activePopup,
|
||||||
});
|
});
|
||||||
@@ -202,6 +204,8 @@ export default function MainView({ className, initService }) {
|
|||||||
let renderingPanels = [];
|
let renderingPanels = [];
|
||||||
if (
|
if (
|
||||||
panels[panels.length - 1]?.name === Config.panel_names.PLAYER_PANEL ||
|
panels[panels.length - 1]?.name === Config.panel_names.PLAYER_PANEL ||
|
||||||
|
panels[panels.length - 1]?.name ===
|
||||||
|
Config.panel_names.PLAYER_PANEL_NEW ||
|
||||||
panels[panels.length - 2]?.name === Config.panel_names.PLAYER_PANEL
|
panels[panels.length - 2]?.name === Config.panel_names.PLAYER_PANEL
|
||||||
) {
|
) {
|
||||||
renderingPanels = panels.slice(-2);
|
renderingPanels = panels.slice(-2);
|
||||||
@@ -212,7 +216,8 @@ export default function MainView({ className, initService }) {
|
|||||||
<>
|
<>
|
||||||
{(isHomeOnTop ||
|
{(isHomeOnTop ||
|
||||||
(panels.length === 1 &&
|
(panels.length === 1 &&
|
||||||
panels[0]?.name === Config.panel_names.PLAYER_PANEL)) && (
|
(panels[0]?.name === Config.panel_names.PLAYER_PANEL ||
|
||||||
|
panels[0]?.name === Config.panel_names.PLAYER_PANEL_NEW))) && (
|
||||||
<HomePanel
|
<HomePanel
|
||||||
key={Config.panel_names.HOME_PANEL}
|
key={Config.panel_names.HOME_PANEL}
|
||||||
isOnTop={isHomeOnTop}
|
isOnTop={isHomeOnTop}
|
||||||
@@ -234,7 +239,9 @@ export default function MainView({ className, initService }) {
|
|||||||
if (
|
if (
|
||||||
index === 0 &&
|
index === 0 &&
|
||||||
renderingPanels.length === 2 &&
|
renderingPanels.length === 2 &&
|
||||||
renderingPanels[1].name === Config.panel_names.PLAYER_PANEL &&
|
(renderingPanels[1].name === Config.panel_names.PLAYER_PANEL ||
|
||||||
|
renderingPanels[1].name ===
|
||||||
|
Config.panel_names.PLAYER_PANEL_NEW) &&
|
||||||
renderingPanels[1].panelInfo.modal
|
renderingPanels[1].panelInfo.modal
|
||||||
) {
|
) {
|
||||||
isPanelOnTop = true;
|
isPanelOnTop = true;
|
||||||
@@ -309,8 +316,8 @@ export default function MainView({ className, initService }) {
|
|||||||
mandatoryIncludeYn: "Y",
|
mandatoryIncludeYn: "Y",
|
||||||
termsList: ["MST00401", "MST00402"],
|
termsList: ["MST00401", "MST00402"],
|
||||||
},
|
},
|
||||||
reload
|
reload,
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}, [dispatch, activePopup]);
|
}, [dispatch, activePopup]);
|
||||||
@@ -362,10 +369,10 @@ export default function MainView({ className, initService }) {
|
|||||||
dispatch(
|
dispatch(
|
||||||
changeAppStatus({
|
changeAppStatus({
|
||||||
cursorVisible: ev.visibility || ev.detail.visibility,
|
cursorVisible: ev.visibility || ev.detail.visibility,
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
[dispatch]
|
[dispatch],
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -377,7 +384,7 @@ export default function MainView({ className, initService }) {
|
|||||||
dispatch(
|
dispatch(
|
||||||
changeAppStatus({
|
changeAppStatus({
|
||||||
cursorVisible: window.cursorEvent && window.cursorEvent.visibility,
|
cursorVisible: window.cursorEvent && window.cursorEvent.visibility,
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return () => {
|
return () => {
|
||||||
@@ -390,7 +397,7 @@ export default function MainView({ className, initService }) {
|
|||||||
const popupTimerRef = useRef(null); // 타이머 ID를 저장할 상태 변수
|
const popupTimerRef = useRef(null); // 타이머 ID를 저장할 상태 변수
|
||||||
|
|
||||||
const { upComingAlertShow } = useSelector(
|
const { upComingAlertShow } = useSelector(
|
||||||
(state) => state.myPage.upComingData
|
(state) => state.myPage.upComingData,
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -423,7 +430,7 @@ export default function MainView({ className, initService }) {
|
|||||||
patnrId: patnrId.toString(),
|
patnrId: patnrId.toString(),
|
||||||
showId,
|
showId,
|
||||||
showNm,
|
showNm,
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}, [activePopup, alertItems, popupVisible]);
|
}, [activePopup, alertItems, popupVisible]);
|
||||||
@@ -465,7 +472,7 @@ export default function MainView({ className, initService }) {
|
|||||||
upComingAlertShow.alertShows?.length > 0
|
upComingAlertShow.alertShows?.length > 0
|
||||||
) {
|
) {
|
||||||
const alertList = upComingAlertShow.alertShows.filter((show) =>
|
const alertList = upComingAlertShow.alertShows.filter((show) =>
|
||||||
isSameDateTime(show.strtDt)
|
isSameDateTime(show.strtDt),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (alertList.length > 0) {
|
if (alertList.length > 0) {
|
||||||
@@ -518,7 +525,7 @@ export default function MainView({ className, initService }) {
|
|||||||
patnrId: patnrId.toString(),
|
patnrId: patnrId.toString(),
|
||||||
showId,
|
showId,
|
||||||
showNm,
|
showNm,
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
dispatch(resetPanels());
|
dispatch(resetPanels());
|
||||||
@@ -526,7 +533,7 @@ export default function MainView({ className, initService }) {
|
|||||||
pushPanel({
|
pushPanel({
|
||||||
name: panel_names.FEATURED_BRANDS_PANEL,
|
name: panel_names.FEATURED_BRANDS_PANEL,
|
||||||
panelInfo: { from: "upcoming", patnrId: patnrId.toString() },
|
panelInfo: { from: "upcoming", patnrId: patnrId.toString() },
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
dispatch(setHidePopup());
|
dispatch(setHidePopup());
|
||||||
setIntervalActive((prev) => !prev);
|
setIntervalActive((prev) => !prev);
|
||||||
@@ -575,7 +582,7 @@ export default function MainView({ className, initService }) {
|
|||||||
patnrId: patnrId.toString(),
|
patnrId: patnrId.toString(),
|
||||||
showId,
|
showId,
|
||||||
showNm,
|
showNm,
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
setIntervalActive((prev) => !prev);
|
setIntervalActive((prev) => !prev);
|
||||||
@@ -639,7 +646,12 @@ export default function MainView({ className, initService }) {
|
|||||||
}
|
}
|
||||||
}, [webOSVersion]);
|
}, [webOSVersion]);
|
||||||
const handleErrorPopupClose = useCallback(() => {
|
const handleErrorPopupClose = useCallback(() => {
|
||||||
console.log('handleErrorPopupClose 호출됨! activePopup:', activePopup, 'popupData:', popupData);
|
console.log(
|
||||||
|
"handleErrorPopupClose 호출됨! activePopup:",
|
||||||
|
activePopup,
|
||||||
|
"popupData:",
|
||||||
|
popupData,
|
||||||
|
);
|
||||||
if (popupData?.shouldPopPanel) {
|
if (popupData?.shouldPopPanel) {
|
||||||
dispatch(popPanel());
|
dispatch(popPanel());
|
||||||
}
|
}
|
||||||
@@ -678,7 +690,7 @@ export default function MainView({ className, initService }) {
|
|||||||
loadingComplete && (
|
loadingComplete && (
|
||||||
<div
|
<div
|
||||||
className={classNames(
|
className={classNames(
|
||||||
css.container
|
css.container,
|
||||||
// showLoadingPanel.type === "launching" ? css.transparent : null
|
// showLoadingPanel.type === "launching" ? css.transparent : null
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
@@ -731,9 +743,10 @@ export default function MainView({ className, initService }) {
|
|||||||
popupData.errorCode,
|
popupData.errorCode,
|
||||||
popupData.errorMsg,
|
popupData.errorMsg,
|
||||||
popupData.retDetailCode,
|
popupData.retDetailCode,
|
||||||
popupData.returnBindStrings
|
popupData.returnBindStrings,
|
||||||
)}
|
)}
|
||||||
</p> <TButton className={css.popupBtn} onClick={handleErrorPopupClose}>
|
</p>{" "}
|
||||||
|
<TButton className={css.popupBtn} onClick={handleErrorPopupClose}>
|
||||||
{$L("OK")}
|
{$L("OK")}
|
||||||
</TButton>
|
</TButton>
|
||||||
</div>
|
</div>
|
||||||
@@ -781,8 +794,6 @@ export default function MainView({ className, initService }) {
|
|||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<SystemNotification />
|
<SystemNotification />
|
||||||
{loadingComplete &&
|
{loadingComplete &&
|
||||||
activePopup === Config.ACTIVE_POPUP.endOfServicePopup &&
|
activePopup === Config.ACTIVE_POPUP.endOfServicePopup &&
|
||||||
|
|||||||
@@ -1535,8 +1535,24 @@ const PlayerPanel = ({
|
|||||||
if (broadcast.type === "videoError") {
|
if (broadcast.type === "videoError") {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For previews, always use the direct URL from panelInfo.
|
||||||
|
if (panelInfo.modal) {
|
||||||
|
return panelInfo.showUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For fullscreen, the playlist is the primary source.
|
||||||
|
if (playListInfo && playListInfo.length > 0 && playListInfo[selectedIndex]?.showUrl) {
|
||||||
|
return playListInfo[selectedIndex]?.showUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback for fullscreen if playlist isn't ready, use panelInfo URL.
|
||||||
|
if (!panelInfo.modal && panelInfo.showUrl) {
|
||||||
|
return panelInfo.showUrl;
|
||||||
|
}
|
||||||
|
|
||||||
return playListInfo && playListInfo[selectedIndex]?.showUrl;
|
return playListInfo && playListInfo[selectedIndex]?.showUrl;
|
||||||
}, [playListInfo, selectedIndex, broadcast]);
|
}, [panelInfo, playListInfo, selectedIndex, broadcast]);
|
||||||
|
|
||||||
const isYoutube = useMemo(() => {
|
const isYoutube = useMemo(() => {
|
||||||
if (currentPlayingUrl && currentPlayingUrl.includes("youtu")) {
|
if (currentPlayingUrl && currentPlayingUrl.includes("youtu")) {
|
||||||
|
|||||||
2281
com.twin.app.shoptime/src/views/PlayerPanel/PlayerPanel.new.jsx
Normal file
2281
com.twin.app.shoptime/src/views/PlayerPanel/PlayerPanel.new.jsx
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,79 @@
|
|||||||
|
@import "../../style/CommonStyle.module.less";
|
||||||
|
@import "../../style/utils.module.less";
|
||||||
|
|
||||||
|
@videoBackgroundColor: black;
|
||||||
|
.videoContainer {
|
||||||
|
position: absolute;
|
||||||
|
background-color: @videoBackgroundColor;
|
||||||
|
z-index: 21;
|
||||||
|
|
||||||
|
.playButton {
|
||||||
|
.size(@w: 60px, @h: 60px);
|
||||||
|
background-image: url("../../../assets/images/btn/btn-video-play-nor@3x.png");
|
||||||
|
background-size: cover;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
.size(@w: 60px, @h: 60px);
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: #c70850;
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.pauseButton {
|
||||||
|
.size(@w: 60px, @h: 60px);
|
||||||
|
background-image: url("../../../assets/images/btn/btn-voc-pause-nor@3x.png");
|
||||||
|
background-size: cover;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
.size(@w: 60px, @h: 60px);
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: #c70850;
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.toOpenBtn {
|
||||||
|
.size(@w: 147px, @h: 243px);
|
||||||
|
min-width: 60px !important;
|
||||||
|
margin: 192px 0 136px 512px;
|
||||||
|
text-align: center;
|
||||||
|
background-image: url(../../../assets/images/btn/btn-toopen-foc.svg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.videoReduce {
|
||||||
|
.size(@w:78px, @h:78px);
|
||||||
|
background: url("../../../assets/images/btn/btn-video-min-nor@3x.png")
|
||||||
|
no-repeat center center/60px 60px;
|
||||||
|
position: absolute;
|
||||||
|
right: 60px;
|
||||||
|
bottom: 150px;
|
||||||
|
z-index: 3;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
.size(@w:78px, @h:78px);
|
||||||
|
background-color: rgba(199, 8, 80, 0.5);
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.modal,
|
||||||
|
&.background {
|
||||||
|
width: 1px;
|
||||||
|
height: 1px;
|
||||||
|
left: -1px;
|
||||||
|
top: -1px;
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 1;
|
||||||
|
background-color: @videoBackgroundColor;
|
||||||
|
overflow: visible;
|
||||||
|
.tabContainer,
|
||||||
|
.arrow,
|
||||||
|
.toOpenBtn {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.hideSubtitle {
|
||||||
|
video::cue {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user