🕐 커밋 시간: 2025. 11. 02. 06:59:07 📊 변경 통계: • 총 파일: 4개 • 추가: +86줄 • 삭제: -35줄 📁 추가된 파일: + com.twin.app.shoptime/src/actions/mockCartActions.js 📝 수정된 파일: ~ com.twin.app.shoptime/src/views/CheckOutPanel/CheckOutPanel.jsx ~ com.twin.app.shoptime/src/views/DetailPanel/components/BuyOption.jsx ~ com.twin.app.shoptime/src/views/MainView/MainView.jsx 🔧 주요 변경 내용: • UI 컴포넌트 아키텍처 개선 • 핵심 비즈니스 로직 개선 • 소규모 기능 개선
869 lines
29 KiB
JavaScript
869 lines
29 KiB
JavaScript
import React, { use, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
|
|
import classNames from 'classnames';
|
|
import { indexOf } from 'ilib/lib/JSUtils';
|
|
import { useDispatch, useSelector } from 'react-redux';
|
|
|
|
import platform from '@enact/core/platform';
|
|
import Spotlight from '@enact/spotlight';
|
|
|
|
import defaultWatchItem from '../../../assets/images/img-alert-banner-st@3x.png';
|
|
// 테스트용 - TODO: 메인 홈 화면에 나와야 하는 이미지들 추가 후 preloadImages에 추가
|
|
import testImage from '../../../assets/images/img-banner-myinfo-login@3x.png';
|
|
import defaultImageItem from '../../../assets/images/img-thumb-empty-product@3x.png';
|
|
import LoadingPreloadImage from '../../../assets/images/intro/splash_02_stop.webp';
|
|
import LoadingAnimation from '../../../assets/images/intro/splash_03_end.webp';
|
|
import LoadingCompleteImage from '../../../assets/images/intro/splash_04_end.webp';
|
|
import LoadingShopOnTvImage from '../../../assets/images/intro/splash_end.jpg';
|
|
import {
|
|
alertToast,
|
|
changeAppStatus,
|
|
changeLocalSettings,
|
|
clearErrorMessage,
|
|
setExitApp,
|
|
setHidePopup,
|
|
setShowPopup,
|
|
} from '../../actions/commonActions';
|
|
import { getHomeMenu, getHomeTerms, updateHomeInfo } from '../../actions/homeActions';
|
|
import {
|
|
sendLogAlarmClick,
|
|
sendLogAlarmPop,
|
|
sendLogLive,
|
|
sendLogVOD,
|
|
} from '../../actions/logActions';
|
|
import { getMyUpcomingAlertShow, setMyTermsWithdraw } from '../../actions/myPageActions';
|
|
import { popPanel, pushPanel, resetPanels, updatePanel } from '../../actions/panelActions';
|
|
import EndOfServicePopUp from '../../components/EndOfServicePopUp/EndOfServicePopUp';
|
|
import Loader from '../../components/Loader/Loader';
|
|
import { convertUtcToLocal } from '../../components/MediaPlayer/util';
|
|
import OptionalTermsConfirm from '../../components/Optional/OptionalTermsConfirm';
|
|
import OptionalTermsConfirmBottom from '../../components/Optional/OptionalTermsConfirmBottom';
|
|
import PreloadImage from '../../components/PreloadImage/PreloadImage';
|
|
import SystemNotification from '../../components/SystemNotification/SystemNotification';
|
|
import TabLayout from '../../components/TabLayout/TabLayout';
|
|
import TButton from '../../components/TButton/TButton';
|
|
import TNewPopUp from '../../components/TPopUp/TNewPopUp';
|
|
import TPopUp from '../../components/TPopUp/TPopUp';
|
|
import usePrevious from '../../hooks/usePrevious';
|
|
import * as Config from '../../utils/Config';
|
|
import { panel_names } from '../../utils/Config';
|
|
import { $L, getErrorMessage, getSpottableDescendants } from '../../utils/helperMethods';
|
|
import { BUYNOW_CONFIG } from '../../utils/BuyNowConfig';
|
|
import { SpotlightIds } from '../../utils/SpotlightIds';
|
|
import CartPanel from '../CartPanel/CartPanel';
|
|
import CategoryPanel from '../CategoryPanel/CategoryPanel';
|
|
import CheckOutPanel from '../CheckOutPanel/CheckOutPanel';
|
|
import ConfirmPanel from '../ConfirmPanel/ConfirmPanel';
|
|
import DebugPanel from '../DebugPanel/DebugPanel';
|
|
import DetailPanel from '../DetailPanel/DetailPanel';
|
|
import ErrorPanel from '../ErrorPanel/ErrorPanel';
|
|
import FeaturedBrandsPanel from '../FeaturedBrandsPanel/FeaturedBrandsPanel';
|
|
import HomePanel from '../HomePanel/HomePanel';
|
|
import HotPicksPanel from '../HotPicksPanel/HotPicksPanel';
|
|
import ImagePanel from '../ImagePanel/ImagePanel';
|
|
import IntroPanel from '../IntroPanel/IntroPanel.new';
|
|
import JustForYouPanel from '../JustForYouPanel/JustForYouPanel';
|
|
import JustForYouTestPanel from '../JustForYouTestPanel/JustForYouTestPanel';
|
|
import LoadingPanel from '../LoadingPanel/LoadingPanel';
|
|
import MediaPanel from '../MediaPanel/MediaPanel';
|
|
import MyPagePanel from '../MyPagePanel/MyPagePanel';
|
|
import OnSalePanel from '../OnSalePanel/OnSalePanel';
|
|
import PlayerPanel from '../PlayerPanel/PlayerPanel';
|
|
import SearchPanel from '../SearchPanel/SearchPanel.new.v2';
|
|
/* VUI_DISABLE_START - VoicePanel import 비활성화 */
|
|
// import VoicePanel from '../VoicePanel/VoicePanel';
|
|
/* VUI_DISABLE_END */
|
|
import ThemeCurationPanel from '../ThemeCurationPanel/ThemeCurationPanel';
|
|
import TrendingNowPanel from '../TrendingNowPanel/TrendingNowPanel';
|
|
import UserReviewPanel from '../UserReview/UserReviewPanel';
|
|
import WelcomeEventPanel from '../WelcomeEventPanel/WelcomeEventPanel';
|
|
import css from './MainView.module.less';
|
|
|
|
const preloadImages = [
|
|
LoadingPreloadImage,
|
|
LoadingAnimation,
|
|
LoadingCompleteImage,
|
|
LoadingShopOnTvImage,
|
|
];
|
|
|
|
const panelMap = {
|
|
[Config.panel_names.INTRO_PANEL]: IntroPanel,
|
|
[Config.panel_names.HOME_PANEL]: HomePanel,
|
|
[Config.panel_names.MY_PAGE_PANEL]: MyPagePanel,
|
|
[Config.panel_names.CATEGORY_PANEL]: CategoryPanel,
|
|
[Config.panel_names.SEARCH_PANEL]: SearchPanel,
|
|
/* VUI_DISABLE_START - VoicePanel panelMap 비활성화 */
|
|
// [Config.panel_names.VOICE_PANEL]: VoicePanel,
|
|
/* VUI_DISABLE_END */
|
|
[Config.panel_names.ON_SALE_PANEL]: OnSalePanel,
|
|
[Config.panel_names.TRENDING_NOW_PANEL]: TrendingNowPanel,
|
|
[Config.panel_names.HOT_PICKS_PANEL]: HotPicksPanel,
|
|
[Config.panel_names.FEATURED_BRANDS_PANEL]: FeaturedBrandsPanel,
|
|
[Config.panel_names.CART_PANEL]: CartPanel,
|
|
[Config.panel_names.ERROR_PANEL]: ErrorPanel,
|
|
[Config.panel_names.DEBUG_PANEL]: DebugPanel,
|
|
[Config.panel_names.DETAIL_PANEL]: DetailPanel,
|
|
[Config.panel_names.PLAYER_PANEL]: PlayerPanel,
|
|
[Config.panel_names.MEDIA_PANEL]: MediaPanel,
|
|
[Config.panel_names.CHECKOUT_PANEL]: CheckOutPanel,
|
|
[Config.panel_names.WELCOME_EVENT_PANEL]: WelcomeEventPanel,
|
|
[Config.panel_names.THEME_CURATION_PANEL]: ThemeCurationPanel,
|
|
[Config.panel_names.IMAGE_PANEL]: ImagePanel,
|
|
[Config.panel_names.CONFIRM_PANEL]: ConfirmPanel,
|
|
[Config.panel_names.USER_REVIEW_PANEL]: UserReviewPanel,
|
|
[Config.panel_names.JUST_FOR_YOU_PANEL]: JustForYouPanel,
|
|
[Config.panel_names.JUST_FOR_YOU_TEST_PANEL]: JustForYouTestPanel,
|
|
// [Config.panel_names.OPTIONAL_TERMS_PANEL]: TermsOfOptional,
|
|
};
|
|
|
|
const logTpNoLiveSet = new Set([
|
|
Config.LOG_TP_NO.LIVE.HOME,
|
|
Config.LOG_TP_NO.LIVE.FEATURED_BRANDS,
|
|
Config.LOG_TP_NO.LIVE.FULL,
|
|
Config.LOG_TP_NO.LIVE.ITEM_DETAIL,
|
|
]);
|
|
|
|
const STRING_CONF = {
|
|
YES: 'YES',
|
|
NO: 'NO',
|
|
EXIT: 'EXIT',
|
|
THIS_IS_AN_UNSUPPORTED_COUNTRY: 'This is an unsupported country.',
|
|
CHANGE_COUNTRY:
|
|
'If you have changed your LG service country setting, the usage information in Shop Time during your previous country setting will be deleted. Do you want to proceed?',
|
|
};
|
|
|
|
export default function MainView({ className, initService }) {
|
|
const dispatch = useDispatch();
|
|
const httpHeader = useSelector((state) => state.common.httpHeader);
|
|
const mainIndex = useSelector((state) => state.appData.mainIndex);
|
|
const panels = useSelector((state) => state.panels.panels);
|
|
const lastPanelAction = useSelector((state) => state.panels.lastPanelAction);
|
|
const loadingComplete = useSelector((state) => state.common?.loadingComplete);
|
|
const menuData = useSelector((state) => state.home.menuData?.data);
|
|
const {
|
|
popupVisible,
|
|
activePopup,
|
|
data: errorCode,
|
|
data: popupData,
|
|
} = useSelector((state) => state.common.popup);
|
|
const { showLoadingPanel, toast, toastText, isLoading, webOSVersion, deviceId } = useSelector(
|
|
(state) => state.common.appStatus
|
|
);
|
|
const skipEndOfServicePopup = useSelector((state) => state.localSettings.skipEndOfServicePopup);
|
|
const isInternetConnected = useSelector((state) => state.common.appStatus.isInternetConnected);
|
|
|
|
const deviceCountryCode = httpHeader?.['X-Device-Country'] || '';
|
|
const isLogSentRef = useRef(false);
|
|
const watchRecord = useSelector((state) => state.localSettings?.watchRecord);
|
|
const watchRecordRef = usePrevious(watchRecord);
|
|
const [tabActivated, setTabActivated] = useState(false);
|
|
const [imagePreloaded, setImagePreloaded] = useState(false);
|
|
const [ariaHidden, setAriaHidden] = useState(false);
|
|
const [showEndOfServicePopup, setShowEndOfServicePopup] = useState(false);
|
|
|
|
const topPanel = panels[panels.length - 1];
|
|
|
|
// BUYNOW_CONFIG 초기화 (마운트 시 한 번만 실행)
|
|
useEffect(() => {
|
|
// TV 배포용: Mock Mode 하드코딩 활성화
|
|
// 모든 상품에서 BUY NOW 버튼이 표시됨
|
|
const mockMode = true;
|
|
|
|
// 모듈 변수에 저장 (이후 ProductAllSection 등에서 메모리에서만 읽음)
|
|
BUYNOW_CONFIG.init(mockMode);
|
|
}, []);
|
|
|
|
// useEffect(() => {
|
|
// console.log('🔍 MainView 팝업 상태 변경:', {
|
|
// popupVisible,
|
|
// activePopup,
|
|
// });
|
|
// }, [popupVisible, activePopup]);
|
|
|
|
const isHomeOnTop = useMemo(() => {
|
|
return !mainIndex && (panels.length <= 0 || (panels.length === 1 && panels[0].panelInfo.modal));
|
|
}, [mainIndex, panels]);
|
|
|
|
const onPreImageLoadComplete = useCallback(() => {
|
|
// console.log('MainView onPreImageLoadComplete');
|
|
dispatch(changeAppStatus({ showLoadingPanel: { show: false } }));
|
|
setImagePreloaded(true);
|
|
}, [dispatch]);
|
|
|
|
const currentTabLayoutPanelInfo = useMemo(() => {
|
|
return panels[panels.length - 1]?.panelInfo;
|
|
}, [panels]);
|
|
|
|
const renderTopPanel = useCallback(() => {
|
|
if (panels && panels.length > 0) {
|
|
let renderingPanels = [];
|
|
|
|
const topPanel = panels[panels.length - 1];
|
|
|
|
// CheckOutPanel은 독립적으로 항상 단독 렌더링
|
|
if (topPanel?.name === Config.panel_names.CHECKOUT_PANEL) {
|
|
console.log('[MainView] CheckOutPanel detected - rendering independently');
|
|
renderingPanels = [topPanel]; // CheckOutPanel만 단독으로 렌더링
|
|
}
|
|
// 기존 3-layer 구조 체크: PlayerPanel + DetailPanel + MediaPanel(modal)
|
|
else {
|
|
const hasThreeLayerStructure =
|
|
panels.length >= 3 &&
|
|
// PlayerPanel이 맨 아래
|
|
(panels[panels.length - 3]?.name === Config.panel_names.PLAYER_PANEL ||
|
|
panels[panels.length - 3]?.name === Config.panel_names.PLAYER_PANEL_NEW) &&
|
|
// DetailPanel이 중간
|
|
panels[panels.length - 2]?.name === Config.panel_names.DETAIL_PANEL &&
|
|
// MediaPanel modal이 맨 위
|
|
panels[panels.length - 1]?.name === Config.panel_names.MEDIA_PANEL &&
|
|
panels[panels.length - 1]?.panelInfo?.modal === true;
|
|
|
|
if (hasThreeLayerStructure) {
|
|
console.log(
|
|
'[MainView] Rendering 3-layer structure: PlayerPanel + DetailPanel + MediaPanel'
|
|
);
|
|
renderingPanels = panels.slice(-3);
|
|
} else if (
|
|
panels[panels.length - 1]?.name === Config.panel_names.PLAYER_PANEL ||
|
|
panels[panels.length - 1]?.name === Config.panel_names.PLAYER_PANEL_NEW ||
|
|
panels[panels.length - 1]?.name === Config.panel_names.MEDIA_PANEL ||
|
|
panels[panels.length - 2]?.name === Config.panel_names.PLAYER_PANEL ||
|
|
panels[panels.length - 2]?.name === Config.panel_names.MEDIA_PANEL
|
|
) {
|
|
renderingPanels = panels.slice(-2);
|
|
} else {
|
|
renderingPanels = panels.slice(-1);
|
|
}
|
|
}
|
|
return (
|
|
<>
|
|
{(isHomeOnTop ||
|
|
(panels.length === 1 &&
|
|
(panels[0]?.name === Config.panel_names.PLAYER_PANEL ||
|
|
panels[0]?.name === Config.panel_names.PLAYER_PANEL_NEW ||
|
|
panels[0]?.name === Config.panel_names.MEDIA_PANEL))) && (
|
|
<HomePanel key={Config.panel_names.HOME_PANEL} isOnTop={isHomeOnTop} />
|
|
)}
|
|
{renderingPanels.map((panel, index) => {
|
|
const Component = panelMap[panel.name];
|
|
let isPanelOnTop = false;
|
|
|
|
// CheckOutPanel은 항상 onTop
|
|
if (panel.name === Config.panel_names.CHECKOUT_PANEL) {
|
|
isPanelOnTop = true;
|
|
console.log('[MainView] CheckOutPanel is always onTop');
|
|
}
|
|
// 3-layer 케이스: 중간 패널(DetailPanel)이 onTop
|
|
else if (renderingPanels.length === 3) {
|
|
if (index === 1) {
|
|
// DetailPanel (중간)
|
|
isPanelOnTop = true;
|
|
console.log('[MainView] 3-layer: DetailPanel is onTop');
|
|
}
|
|
// PlayerPanel (index 0): isOnTop = false (백그라운드)
|
|
// MediaPanel (index 2): isOnTop = false (modal overlay)
|
|
}
|
|
// 2-layer 케이스: modal이면 첫 번째가 onTop
|
|
else if (
|
|
index === 0 &&
|
|
renderingPanels.length === 2 &&
|
|
(renderingPanels[1].name === Config.panel_names.PLAYER_PANEL ||
|
|
renderingPanels[1].name === Config.panel_names.PLAYER_PANEL_NEW ||
|
|
renderingPanels[1].name === Config.panel_names.MEDIA_PANEL) &&
|
|
renderingPanels[1].panelInfo.modal
|
|
) {
|
|
isPanelOnTop = true;
|
|
}
|
|
// 일반 케이스: 마지막 패널이 onTop
|
|
else if (index === renderingPanels.length - 1) {
|
|
isPanelOnTop = true;
|
|
}
|
|
|
|
return (
|
|
<Component
|
|
key={panel.name + panels.indexOf(panel)} //to maintain react virtual dom
|
|
panelInfo={panel.panelInfo}
|
|
spotlightId={panel.name}
|
|
isTabActivated={tabActivated}
|
|
isOnTop={isPanelOnTop}
|
|
/>
|
|
);
|
|
})}
|
|
</>
|
|
);
|
|
} else if (isHomeOnTop) {
|
|
return <HomePanel key={Config.panel_names.HOME_PANEL} isOnTop={isHomeOnTop} />;
|
|
}
|
|
|
|
return null;
|
|
}, [panels, tabActivated, isHomeOnTop]);
|
|
|
|
const onTabActivated = useCallback((activated) => {
|
|
setTabActivated(activated);
|
|
}, []);
|
|
|
|
const topPanelName = useMemo(() => {
|
|
if (panels && panels.length > 0) {
|
|
let targetName = panels[panels.length - 1].name;
|
|
if (
|
|
(panels[panels.length - 1].name === Config.panel_names.PLAYER_PANEL ||
|
|
panels[panels.length - 1].name === Config.panel_names.MEDIA_PANEL) &&
|
|
panels[panels.length - 1].panelInfo.modal
|
|
) {
|
|
targetName = panels[panels.length - 2]?.name;
|
|
}
|
|
return targetName;
|
|
}
|
|
|
|
return null;
|
|
}, [panels]);
|
|
|
|
function reload(response) {
|
|
if (response) {
|
|
if (response.retCode === 0 && typeof window === 'object') {
|
|
window.location.reload();
|
|
} else {
|
|
console.error('unknown error', response.retCode);
|
|
}
|
|
}
|
|
}
|
|
|
|
const handlePopupClick = useCallback(() => {
|
|
// 602 지원하지 않는 국가
|
|
if (activePopup === Config.ACTIVE_POPUP.unSupportedCountryPopup) {
|
|
dispatch(setExitApp());
|
|
return;
|
|
}
|
|
|
|
// 603 국가 변경
|
|
if (activePopup === Config.ACTIVE_POPUP.changeCountyPopup) {
|
|
dispatch(
|
|
setMyTermsWithdraw(
|
|
{
|
|
mandatoryIncludeYn: 'Y',
|
|
termsList: ['MST00401', 'MST00402'],
|
|
},
|
|
reload
|
|
)
|
|
);
|
|
}
|
|
}, [dispatch, activePopup]);
|
|
|
|
const handlePopupClose = useCallback(() => {
|
|
dispatch(setExitApp());
|
|
}, [dispatch]);
|
|
|
|
const getMessageByPopupType = (type) => {
|
|
switch (type) {
|
|
case Config.ACTIVE_POPUP.unSupportedCountryPopup:
|
|
return $L(STRING_CONF.THIS_IS_AN_UNSUPPORTED_COUNTRY);
|
|
|
|
case Config.ACTIVE_POPUP.changeCountyPopup:
|
|
return $L(STRING_CONF.CHANGE_COUNTRY);
|
|
|
|
default:
|
|
return;
|
|
}
|
|
};
|
|
|
|
useEffect(() => {
|
|
if (panels && panels.length > 0) {
|
|
let panel = panels[panels.length - 1];
|
|
|
|
// 3-layer 구조 체크: PlayerPanel + DetailPanel + MediaPanel(modal)
|
|
if (
|
|
panels.length >= 3 &&
|
|
panels[panels.length - 1]?.name === Config.panel_names.MEDIA_PANEL &&
|
|
panels[panels.length - 1]?.panelInfo?.modal &&
|
|
panels[panels.length - 2]?.name === Config.panel_names.DETAIL_PANEL
|
|
) {
|
|
panel = panels[panels.length - 2]; // DetailPanel로 포커스
|
|
console.log('[MainView] 3-layer: Focus on DetailPanel');
|
|
}
|
|
// 2-layer modal 구조
|
|
else if (
|
|
(panels[panels.length - 1].name === Config.panel_names.PLAYER_PANEL ||
|
|
panels[panels.length - 1].name === Config.panel_names.MEDIA_PANEL) &&
|
|
panels[panels.length - 1].panelInfo.modal
|
|
) {
|
|
panel = panels[panels.length - 2];
|
|
}
|
|
|
|
if (panel?.name) {
|
|
const timeoutId = setTimeout(() => {
|
|
// focus to panel if it's not preview video status changing case
|
|
if (
|
|
!lastPanelAction ||
|
|
(lastPanelAction.indexOf('preview') !== 0 && lastPanelAction !== 'update')
|
|
) {
|
|
//do focus itself if has focusedContainerId
|
|
Spotlight.focus(panel?.name);
|
|
}
|
|
}, 0);
|
|
|
|
return () => clearTimeout(timeoutId);
|
|
}
|
|
}
|
|
}, [panels, lastPanelAction]);
|
|
|
|
const cursorStateChange = useCallback(
|
|
(ev) => {
|
|
dispatch(
|
|
changeAppStatus({
|
|
cursorVisible: ev.visibility || ev.detail.visibility,
|
|
})
|
|
);
|
|
},
|
|
[dispatch]
|
|
);
|
|
|
|
useEffect(() => {
|
|
document.addEventListener('cursorStateChange', cursorStateChange, false);
|
|
if (platform.platformName !== 'webos') {
|
|
//for debug
|
|
dispatch(changeAppStatus({ cursorVisible: !platform.touchscreen }));
|
|
} else {
|
|
dispatch(
|
|
changeAppStatus({
|
|
cursorVisible: window.cursorEvent && window.cursorEvent.visibility,
|
|
})
|
|
);
|
|
}
|
|
return () => {
|
|
document.removeEventListener('cursorStateChange', cursorStateChange);
|
|
};
|
|
}, []);
|
|
|
|
const [intervalActive, setIntervalActive] = useState(true);
|
|
const [alertItems, setAlertItems] = useState([]);
|
|
const popupTimerRef = useRef(null); // 타이머 ID를 저장할 상태 변수
|
|
|
|
const { upComingAlertShow } = useSelector((state) => state.myPage.upComingData);
|
|
const upComingAlertShowRef = useRef(upComingAlertShow);
|
|
const firstAlertItem = useMemo(() => alertItems[0], [alertItems]);
|
|
|
|
useEffect(() => {
|
|
upComingAlertShowRef.current = upComingAlertShow;
|
|
}, [upComingAlertShow]);
|
|
|
|
useEffect(() => {
|
|
if (activePopup === Config.ACTIVE_POPUP.watchPopup && firstAlertItem && popupVisible) {
|
|
const {
|
|
hstNm,
|
|
lgCatCd,
|
|
lgCatNm,
|
|
patncNm,
|
|
patnrId,
|
|
showId,
|
|
showNm,
|
|
strtDt: alarmDt,
|
|
} = firstAlertItem;
|
|
|
|
dispatch(
|
|
sendLogAlarmPop({
|
|
alarmDt,
|
|
alarmType: 'Upcoming',
|
|
cnt: '0',
|
|
hstNm,
|
|
lgCatCd,
|
|
lgCatNm,
|
|
patncNm,
|
|
patnrId: patnrId.toString(),
|
|
showId,
|
|
showNm,
|
|
})
|
|
);
|
|
}
|
|
}, [activePopup, firstAlertItem, popupVisible, dispatch]);
|
|
|
|
// 팝업 30초 후 종료
|
|
useEffect(() => {
|
|
// 이전 타이머 정리
|
|
if (popupTimerRef.current) {
|
|
clearTimeout(popupTimerRef.current);
|
|
popupTimerRef.current = null;
|
|
}
|
|
|
|
if (popupVisible && activePopup === Config.ACTIVE_POPUP.watchPopup) {
|
|
const timerId = setTimeout(() => {
|
|
dispatch(setHidePopup());
|
|
setIntervalActive((prev) => !prev);
|
|
}, 30000);
|
|
// 타이머 ID를 상태 변수에 저장
|
|
popupTimerRef.current = timerId;
|
|
}
|
|
|
|
return () => {
|
|
if (popupTimerRef.current) {
|
|
clearTimeout(popupTimerRef.current);
|
|
popupTimerRef.current = null;
|
|
}
|
|
};
|
|
}, [popupVisible, activePopup, dispatch]);
|
|
|
|
// 알림 팝업
|
|
const checkForAlerts = useCallback(() => {
|
|
const currentUpComingAlertShow = upComingAlertShowRef.current;
|
|
if (
|
|
currentUpComingAlertShow?.upcomAlamUseFlag === 'Y' &&
|
|
currentUpComingAlertShow.alertShows?.length > 0
|
|
) {
|
|
const alertList = currentUpComingAlertShow.alertShows.filter((show) =>
|
|
isSameDateTime(show.strtDt)
|
|
);
|
|
|
|
if (alertList.length > 0) {
|
|
setAlertItems(alertList);
|
|
dispatch(setShowPopup(Config.ACTIVE_POPUP.watchPopup));
|
|
setIntervalActive((prev) => !prev);
|
|
}
|
|
}
|
|
}, [dispatch]);
|
|
|
|
// 알람 체크 - 50초 마다 시간 체크(분까지만 체크)
|
|
useEffect(() => {
|
|
const interval = setInterval(() => {
|
|
if (intervalActive) {
|
|
checkForAlerts();
|
|
}
|
|
}, 50000);
|
|
return () => clearInterval(interval);
|
|
}, [intervalActive, checkForAlerts]);
|
|
|
|
// 날짜 같은지 확인 하는 함수
|
|
const isSameDateTime = (dateString) => {
|
|
const today = new Date();
|
|
const targetDate = convertUtcToLocal(dateString);
|
|
|
|
return (
|
|
today.getFullYear() === targetDate.getFullYear() &&
|
|
today.getMonth() === targetDate.getMonth() &&
|
|
today.getDate() === targetDate.getDate() &&
|
|
today.getHours() === targetDate.getHours() &&
|
|
today.getMinutes() === targetDate.getMinutes()
|
|
);
|
|
};
|
|
|
|
// 알림 확인 FEATURED_BRANDS_PANEL 이동
|
|
const handleClick = useCallback(() => {
|
|
if (!firstAlertItem) return;
|
|
|
|
const {
|
|
hstNm,
|
|
lgCatCd,
|
|
lgCatNm,
|
|
patncNm,
|
|
patnrId,
|
|
showId,
|
|
showNm,
|
|
strtDt: alarmDt,
|
|
} = firstAlertItem;
|
|
|
|
dispatch(
|
|
sendLogAlarmClick({
|
|
alarmDt,
|
|
alarmType: 'Upcoming',
|
|
clickFlag: 'Ok',
|
|
cnt: '0',
|
|
hstNm,
|
|
keywordList: '',
|
|
lgCatCd,
|
|
lgCatNm,
|
|
logTpNo: Config.LOG_TP_NO.ALARM_CLICK.BROADCAST,
|
|
patncNm,
|
|
patnrId: patnrId.toString(),
|
|
showId,
|
|
showNm,
|
|
})
|
|
);
|
|
|
|
dispatch(resetPanels());
|
|
dispatch(
|
|
pushPanel({
|
|
name: panel_names.FEATURED_BRANDS_PANEL,
|
|
panelInfo: { from: 'upcoming', patnrId: patnrId.toString() },
|
|
})
|
|
);
|
|
dispatch(setHidePopup());
|
|
setIntervalActive((prev) => !prev);
|
|
|
|
if (popupTimerRef.current) {
|
|
clearTimeout(popupTimerRef.current); // 버튼 클릭 시 타이머 제거
|
|
popupTimerRef.current = null;
|
|
}
|
|
}, [dispatch, firstAlertItem]);
|
|
|
|
const onClickNetworkError = useCallback(() => {
|
|
if (initService) {
|
|
initService();
|
|
}
|
|
dispatch(setHidePopup());
|
|
}, [dispatch, initService]);
|
|
|
|
const onExitNetworkError = useCallback(() => {
|
|
dispatch(setExitApp());
|
|
}, [dispatch]);
|
|
|
|
const onWatchClose = useCallback(() => {
|
|
if (!firstAlertItem) return;
|
|
|
|
const {
|
|
hstNm,
|
|
lgCatCd,
|
|
lgCatNm,
|
|
patncNm,
|
|
patnrId,
|
|
showId,
|
|
showNm,
|
|
strtDt: alarmDt,
|
|
} = firstAlertItem;
|
|
|
|
dispatch(
|
|
sendLogAlarmClick({
|
|
alarmDt,
|
|
alarmType: 'Upcoming',
|
|
clickFlag: 'No',
|
|
cnt: '0',
|
|
hstNm,
|
|
keywordList: '',
|
|
lgCatCd,
|
|
lgCatNm,
|
|
logTpNo: Config.LOG_TP_NO.ALARM_CLICK.BROADCAST,
|
|
patncNm,
|
|
patnrId: patnrId.toString(),
|
|
showId,
|
|
showNm,
|
|
})
|
|
);
|
|
|
|
setIntervalActive((prev) => !prev);
|
|
dispatch(setHidePopup());
|
|
}, [firstAlertItem, dispatch]);
|
|
|
|
const sendLogIfNeeded = useCallback(() => {
|
|
if (!watchRecordRef.current || Object.keys(watchRecordRef.current).length === 0) {
|
|
return;
|
|
}
|
|
|
|
const { logTpNo } = watchRecordRef.current;
|
|
const sendLog = logTpNoLiveSet.has(logTpNo) ? sendLogLive : sendLogVOD;
|
|
const resetWatchRecord = () => dispatch(changeLocalSettings({ watchRecord: {} }));
|
|
|
|
dispatch(sendLog({ ...watchRecordRef.current }, resetWatchRecord));
|
|
}, [dispatch]);
|
|
|
|
useEffect(() => {
|
|
if (deviceId && httpHeader && !isLogSentRef.current) {
|
|
sendLogIfNeeded();
|
|
isLogSentRef.current = true;
|
|
}
|
|
}, [deviceId, httpHeader, sendLogIfNeeded]);
|
|
|
|
useEffect(() => {
|
|
if (loadingComplete && !menuData) {
|
|
dispatch(getHomeMenu());
|
|
}
|
|
}, [loadingComplete]);
|
|
|
|
useEffect(() => {
|
|
if (
|
|
activePopup === Config.ACTIVE_POPUP.changeCountyPopup ||
|
|
activePopup === Config.ACTIVE_POPUP.unSupportedCountryPopup
|
|
) {
|
|
const timeoutId = setTimeout(() => {
|
|
Spotlight.focus('tPopupBtn1');
|
|
}, 0);
|
|
|
|
return () => clearTimeout(timeoutId);
|
|
}
|
|
}, [activePopup]);
|
|
|
|
useEffect(() => {
|
|
if (deviceCountryCode === 'US') {
|
|
setAriaHidden(false);
|
|
} else {
|
|
setAriaHidden(true);
|
|
}
|
|
}, [deviceCountryCode]);
|
|
|
|
useEffect(() => {
|
|
if (webOSVersion && Number(webOSVersion) < 4 && !skipEndOfServicePopup) {
|
|
setShowEndOfServicePopup(true);
|
|
dispatch(setShowPopup(Config.ACTIVE_POPUP.endOfServicePopup));
|
|
}
|
|
}, [webOSVersion]);
|
|
const handleErrorPopupClose = useCallback(() => {
|
|
console.log('handleErrorPopupClose 호출됨! activePopup:', activePopup, 'popupData:', popupData);
|
|
if (popupData?.shouldPopPanel) {
|
|
dispatch(popPanel());
|
|
}
|
|
|
|
dispatch(clearErrorMessage());
|
|
dispatch(setHidePopup());
|
|
|
|
if (topPanel?.name === panel_names.DETAIL_PANEL) {
|
|
setTimeout(() => {
|
|
Spotlight.focus(SpotlightIds.DETAIL_BUYNOW);
|
|
});
|
|
}
|
|
}, [dispatch, popupData, activePopup, topPanel?.name]);
|
|
|
|
// 딥링크 확인 테스트
|
|
const contentTarget = useSelector((state) => state.common.deepLinkInfo.contentTarget);
|
|
return (
|
|
<div className={classNames(css.mainViewWrap, className)} aria-hidden={ariaHidden}>
|
|
{!imagePreloaded && (
|
|
<PreloadImage preloadImages={preloadImages} onLoadComplete={onPreImageLoadComplete} />
|
|
)}
|
|
<LoadingPanel showLoadingPanel={showLoadingPanel} />
|
|
{errorCode !== 502 && errorCode !== 602 && errorCode !== 603 && loadingComplete && (
|
|
<div className={classNames(css.container)}>
|
|
<div className={classNames(css.mainlayout)}>{renderTopPanel()}</div>
|
|
<TabLayout
|
|
topPanelName={topPanelName}
|
|
onTabActivated={onTabActivated}
|
|
panelInfo={currentTabLayoutPanelInfo}
|
|
/>
|
|
</div>
|
|
)}
|
|
{isLoading && <Loader />}
|
|
|
|
{/* 시청예약 알림 팝업 */}
|
|
{activePopup === Config.ACTIVE_POPUP.watchPopup && firstAlertItem && (
|
|
<TPopUp
|
|
kind="watchPopup"
|
|
open={popupVisible}
|
|
onClose={onWatchClose}
|
|
onClick={handleClick}
|
|
hasText
|
|
hasButton
|
|
hasLogo
|
|
logo={firstAlertItem?.patncLogoPath}
|
|
hasThumbnail
|
|
thumbnail={
|
|
firstAlertItem?.thumbnailUrl == null || firstAlertItem?.thumbnailUrl == ''
|
|
? defaultWatchItem
|
|
: firstAlertItem?.thumbnailUrl
|
|
}
|
|
button1Text={$L('YES')}
|
|
button2Text={$L('NO')}
|
|
title={$L('Watch Now!')}
|
|
text={firstAlertItem?.showNm}
|
|
/>
|
|
)}
|
|
|
|
{activePopup === Config.ACTIVE_POPUP.errorPopup && (
|
|
<TPopUp
|
|
kind="errorPopup"
|
|
open={popupVisible}
|
|
onClose={handleErrorPopupClose}
|
|
className={css.errorPopup}
|
|
>
|
|
<div className={css.popupContainer}>
|
|
<p className={css.errMsg}>
|
|
{popupData &&
|
|
getErrorMessage(
|
|
popupData.errorCode,
|
|
popupData.errorMsg,
|
|
popupData.retDetailCode,
|
|
popupData.returnBindStrings
|
|
)}
|
|
</p>{' '}
|
|
<TButton className={css.popupBtn} onClick={handleErrorPopupClose}>
|
|
{$L('OK')}
|
|
</TButton>
|
|
</div>
|
|
</TPopUp>
|
|
)}
|
|
|
|
{activePopup === Config.ACTIVE_POPUP.networkErrorPopup && (
|
|
<TPopUp
|
|
kind="textPopup"
|
|
hasButton
|
|
hasText
|
|
button1Text={isInternetConnected ? 'Retry' : ''}
|
|
button2Text={'Exit'}
|
|
onClick={onClickNetworkError}
|
|
onClose={onExitNetworkError}
|
|
open={popupVisible}
|
|
text={"Failed to connect to SHOPTIME. Please check your device's network connection."}
|
|
/>
|
|
)}
|
|
|
|
{(loadingComplete && activePopup === Config.ACTIVE_POPUP.unSupportedCountryPopup) ||
|
|
(loadingComplete && activePopup === Config.ACTIVE_POPUP.changeCountyPopup) ? (
|
|
<TPopUp
|
|
kind="textPopup"
|
|
hasButton
|
|
hasText
|
|
button1Text={
|
|
activePopup !== Config.ACTIVE_POPUP.unSupportedCountryPopup
|
|
? $L(STRING_CONF.YES)
|
|
: $L(STRING_CONF.EXIT)
|
|
}
|
|
button2Text={activePopup == Config.ACTIVE_POPUP.changeCountyPopup && $L(STRING_CONF.NO)}
|
|
onClick={handlePopupClick}
|
|
onClose={handlePopupClose}
|
|
open={popupVisible}
|
|
text={getMessageByPopupType(activePopup)}
|
|
title={$L('Exit Shop Time')}
|
|
/>
|
|
) : null}
|
|
|
|
<SystemNotification />
|
|
{loadingComplete &&
|
|
activePopup === Config.ACTIVE_POPUP.endOfServicePopup &&
|
|
!skipEndOfServicePopup && <EndOfServicePopUp />}
|
|
|
|
{/* Pop up */}
|
|
{activePopup === Config.ACTIVE_POPUP.toast && (
|
|
<TPopUp
|
|
kind="toast"
|
|
open={popupVisible}
|
|
onClose={handleErrorPopupClose}
|
|
hasText
|
|
hasButton
|
|
button1Text={toastText}
|
|
button2Text={$L('OK')}
|
|
/>
|
|
)}
|
|
{/* OptionalTermsConfirmPopup */}
|
|
{activePopup === Config.ACTIVE_POPUP.optionalTermsConfirm && (
|
|
<OptionalTermsConfirm open={popupVisible} />
|
|
)}
|
|
{activePopup === Config.ACTIVE_POPUP.optionalTermsConfirmBottom && (
|
|
<OptionalTermsConfirmBottom open={popupVisible} />
|
|
)}
|
|
{/* {activePopup === Config.ACTIVE_POPUP.optionalConfirm && (
|
|
<TNewPopUp
|
|
kind="optionalConfirm"
|
|
open={true}
|
|
text="Get recommendations, special offers, and ads tailored just for you."
|
|
button1Text="Agree"
|
|
button2Text="Not Now"
|
|
onClose={handleClose}
|
|
onClick={handleAgree}
|
|
/>
|
|
)} */}
|
|
{/* /딥링크 테스트 */}
|
|
<div
|
|
style={{
|
|
position: 'fixed',
|
|
left: '0',
|
|
bottom: '0',
|
|
color: '#fff',
|
|
fontSize: '13px',
|
|
opacity: '.5',
|
|
zIndex: '999',
|
|
backgroundColor: 'black',
|
|
padding: '10px',
|
|
}}
|
|
>
|
|
deepLinkInfo
|
|
<p>{contentTarget}</p>
|
|
<p>v1030-001</p>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|