🕐 커밋 시간: 2025. 11. 22. 05:46:17 📊 변경 통계: • 총 파일: 2개 • 추가: +85줄 • 삭제: -42줄 📝 수정된 파일: ~ com.twin.app.shoptime/src/components/VideoPlayer/VideoPlayer.v3.js ~ com.twin.app.shoptime/src/views/MediaPanel/MediaPanel.v3.jsx 🔧 함수 변경 내용: 📄 com.twin.app.shoptime/src/views/MediaPanel/MediaPanel.v3.jsx (javascript): 🔄 Modified: normalizeModalStyle() 🔧 주요 변경 내용: • UI 컴포넌트 아키텍처 개선
2461 lines
78 KiB
JavaScript
2461 lines
78 KiB
JavaScript
import React, {
|
|
useCallback,
|
|
useEffect,
|
|
useImperativeHandle,
|
|
useLayoutEffect,
|
|
useMemo,
|
|
useRef,
|
|
} from 'react';
|
|
|
|
import classNames from 'classnames';
|
|
import { useDispatch } from 'react-redux';
|
|
|
|
import { Job } from '@enact/core/util';
|
|
import Spotlight from '@enact/spotlight';
|
|
import SpotlightContainerDecorator from '@enact/spotlight/SpotlightContainerDecorator';
|
|
import { setContainerLastFocusedElement } from '@enact/spotlight/src/container';
|
|
|
|
import dummyVtt from '../../../assets/mock/video.vtt';
|
|
import {
|
|
changeAppStatus,
|
|
changeLocalSettings,
|
|
requestLiveSubtitle,
|
|
sendBroadCast,
|
|
setHidePopup,
|
|
} from '../../actions/commonActions';
|
|
import {
|
|
sendLogGNB,
|
|
sendLogLive,
|
|
sendLogTotalRecommend,
|
|
sendLogVOD,
|
|
} from '../../actions/logActions';
|
|
import {
|
|
clearShopNowInfo,
|
|
getHomeFullVideoInfo,
|
|
getMainCategoryShowDetail,
|
|
getMainLiveShow,
|
|
getMainLiveShowNowProduct,
|
|
} from '../../actions/mainActions';
|
|
import * as PanelActions from '../../actions/panelActions';
|
|
import { updatePanel, focusPanel } from '../../actions/panelActions';
|
|
import {
|
|
CLEAR_PLAYER_INFO,
|
|
getChatLog,
|
|
getSubTitle,
|
|
startVideoPlayer,
|
|
} from '../../actions/playActions';
|
|
import { resetPlayerOverlays } from '../../actions/videoPlayActions';
|
|
import {
|
|
finishModalMediaForce,
|
|
minimizeModalMedia,
|
|
pauseModalMedia,
|
|
restoreModalMedia,
|
|
resumeModalMedia,
|
|
startMediaPlayer,
|
|
switchMediaToFullscreen,
|
|
switchMediaToModal,
|
|
} from '../../actions/mediaActions';
|
|
import { convertUtcToLocal } from '../../components/MediaPlayer/util';
|
|
import TPanel from '../../components/TPanel/TPanel';
|
|
import TPopUp from '../../components/TPopUp/TPopUp';
|
|
import Media from '../../components/VideoPlayer/Media';
|
|
import TReactPlayer from '../../components/VideoPlayer/TReactPlayer';
|
|
import { VideoPlayer } from '../../components/VideoPlayer/VideoPlayer.v3';
|
|
import MediaOverlayContents from '../PlayerPanel/PlayerOverlay/MediaOverlayContents';
|
|
import usePrevious from '../../hooks/usePrevious';
|
|
import useWhyDidYouUpdate from '../../hooks/useWhyDidYouUpdate';
|
|
import * as Config from '../../utils/Config';
|
|
import { ACTIVE_POPUP, panel_names } from '../../utils/Config';
|
|
import { $L, formatGMTString } from '../../utils/helperMethods';
|
|
import { SpotlightIds } from '../../utils/SpotlightIds';
|
|
import css from './MediaPanel.v3.module.less';
|
|
|
|
const Container = SpotlightContainerDecorator(
|
|
{ enterTo: 'default-element', preserveld: true },
|
|
'div'
|
|
);
|
|
|
|
const findSelector = (selector, maxAttempts = 5, currentAttempts = 0) => {
|
|
try {
|
|
if (currentAttempts >= maxAttempts) {
|
|
throw new Error('selector not found');
|
|
}
|
|
|
|
const initialSelector = document.querySelector(selector);
|
|
|
|
if (initialSelector) {
|
|
return initialSelector;
|
|
} else {
|
|
return findSelector(selector, maxAttempts, currentAttempts + 1);
|
|
}
|
|
} catch (error) {
|
|
// console.error(error.message);
|
|
}
|
|
};
|
|
|
|
const getLogTpNo = (type, nowMenu) => {
|
|
if (type === 'LIVE') {
|
|
switch (nowMenu) {
|
|
case Config.LOG_MENU.HOME_TOP:
|
|
return Config.LOG_TP_NO.LIVE.HOME;
|
|
// case Config.LOG_MENU.FEATURED_BRANDS_LIVE_CHANNELS:
|
|
// return Config.LOG_TP_NO.LIVE.FEATURED_BRANDS;
|
|
case Config.LOG_MENU.FULL_SHOP_NOW:
|
|
case Config.LOG_MENU.FULL_YOU_MAY_LIKE:
|
|
case Config.LOG_MENU.FULL_LIVE_CHANNELS:
|
|
return Config.LOG_TP_NO.LIVE.FULL;
|
|
default:
|
|
return Config.LOG_TP_NO.LIVE.FEATURED_BRANDS;
|
|
}
|
|
} else if (type === 'VOD') {
|
|
switch (nowMenu) {
|
|
case Config.LOG_MENU.HOME_TOP:
|
|
return Config.LOG_TP_NO.VOD.HOME_VOD; // 153
|
|
case Config.LOG_MENU.TRENDING_NOW_POPULAR_SHOWS:
|
|
return Config.LOG_TP_NO.VOD.POPULAR_SHOWS_AND_HOT_PICKS; // 151
|
|
case Config.LOG_MENU.FULL_SHOP_NOW:
|
|
case Config.LOG_MENU.FULL_YOU_MAY_LIKE:
|
|
case Config.LOG_MENU.FULL_FEATURED_SHOWS:
|
|
return Config.LOG_TP_NO.VOD.FULL_VOD; // 150
|
|
default:
|
|
return;
|
|
}
|
|
} else if (type === 'MEDIA') {
|
|
switch (nowMenu) {
|
|
case Config.LOG_MENU.HOME_TOP:
|
|
return Config.LOG_TP_NO.VOD.HOME_VOD; // 153
|
|
case Config.LOG_MENU.HOT_PICKS:
|
|
return Config.LOG_TP_NO.VOD.POPULAR_SHOWS_AND_HOT_PICKS; // 151
|
|
case Config.LOG_MENU.DETAIL_PAGE_BILLING_PRODUCT_DETAIL:
|
|
case Config.LOG_MENU.DETAIL_PAGE_PRODUCT_DETAIL:
|
|
case Config.LOG_MENU.DETAIL_PAGE_GROUP_DETAIL:
|
|
case Config.LOG_MENU.DETAIL_PAGE_THEME_DETAIL:
|
|
case Config.LOG_MENU.DETAIL_PAGE_TRAVEL_THEME_DETAIL:
|
|
return Config.LOG_TP_NO.VOD.ITEM_DETAIL_MEDIA; // 156
|
|
case Config.LOG_MENU.FULL:
|
|
return Config.LOG_TP_NO.VOD.FULL_MEDIA; // 155
|
|
default:
|
|
return;
|
|
}
|
|
}
|
|
};
|
|
|
|
const YOUTUBECONFIG = {
|
|
playerVars: {
|
|
controls: 0, // 플레이어 컨트롤 표시
|
|
autoplay: 1,
|
|
disablekb: 1,
|
|
enablejsapi: 1,
|
|
listType: 'user_uploads',
|
|
fs: 0,
|
|
rel: 0, // 관련 동영상 표시 안 함
|
|
showinfo: 0,
|
|
loop: 0,
|
|
iv_load_policy: 3,
|
|
modestbranding: 1,
|
|
wmode: 'opaque',
|
|
cc_lang_pref: 'en',
|
|
cc_load_policy: 0,
|
|
playsinline: 1,
|
|
},
|
|
};
|
|
|
|
// last time error
|
|
const VIDEO_END_ACTION_DELAY = 1500;
|
|
|
|
const VIDEO_MARGIN = 4;
|
|
|
|
const normalizeModalStyle = (style = {}) => {
|
|
if (!style || typeof style !== 'object') {
|
|
return {};
|
|
}
|
|
|
|
const parseValue = (value) => {
|
|
if (typeof value === 'number') {
|
|
return value;
|
|
}
|
|
if (typeof value === 'string') {
|
|
const parsed = parseFloat(value);
|
|
return Number.isNaN(parsed) ? 0 : parsed;
|
|
}
|
|
return 0;
|
|
};
|
|
|
|
const baseWidth = parseValue(style.width);
|
|
const baseHeight = parseValue(style.height);
|
|
const baseLeft = parseValue(style.left);
|
|
const baseTop = parseValue(style.top);
|
|
|
|
const adjustedWidth = Math.max(baseWidth - VIDEO_MARGIN * 2, 0);
|
|
const adjustedHeight = Math.max(baseHeight - VIDEO_MARGIN * 2, 0);
|
|
const adjustedLeft = baseLeft + VIDEO_MARGIN;
|
|
const adjustedTop = baseTop + VIDEO_MARGIN;
|
|
|
|
return {
|
|
...style,
|
|
width: baseWidth ? `${adjustedWidth}px` : style.width,
|
|
height: baseHeight ? `${adjustedHeight}px` : style.height,
|
|
left: style.left !== undefined ? `${adjustedLeft}px` : style.left,
|
|
top: style.top !== undefined ? `${adjustedTop}px` : style.top,
|
|
};
|
|
};
|
|
|
|
const MediaPanel = React.forwardRef(
|
|
({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props }, ref) => {
|
|
const dispatch = useDispatch();
|
|
const { USE_STATE, USE_SELECTOR } = useWhyDidYouUpdate(spotlightId, {
|
|
isTabActivated,
|
|
panelInfo,
|
|
isOnTop,
|
|
...props,
|
|
});
|
|
|
|
const videoPlayer = useRef(null);
|
|
const focusReturnRef = useRef(null);
|
|
const isTransitioningToFullscreen = useRef(false);
|
|
const [playListInfo, setPlayListInfo] = USE_STATE('playListInfo', '');
|
|
const [shopNowInfo, setShopNowInfo] = USE_STATE('shopNowInfo');
|
|
const [backupInitialIndex, setBackupInitialIndex] = USE_STATE('backupInitialIndex', 0);
|
|
const [modalStyle, setModalStyle] = USE_STATE('modalStyle', {});
|
|
const [modalScale, setModalScale] = USE_STATE('modalScale', 1);
|
|
const [mediaId, setMediaId] = USE_STATE('mediaId', null);
|
|
const [currentLiveTimeSeconds, setCurrentLiveTimeSeconds] = USE_STATE(
|
|
'currentLiveTimeSeconds',
|
|
1
|
|
);
|
|
const [prevChannelIndex, setPrevChannelIndex] = USE_STATE('prevChannelIndex', 0);
|
|
const [currentTime, setCurrentTime] = USE_STATE('currentTime', 0);
|
|
const [isInitialFocusOccurred, setIsInitialFocusOccurred] = USE_STATE(
|
|
'isInitialFocusOccurred',
|
|
false
|
|
);
|
|
const [selectedIndex, setSelectedIndex] = USE_STATE(
|
|
'selectedIndex',
|
|
panelInfo.shptmBanrTpNm === 'LIVE' ? null : 0
|
|
);
|
|
const [isUpdate, setIsUpdate] = USE_STATE('isUpdate', false);
|
|
const [isSubtitleActive, setIsSubtitleActive] = USE_STATE('isSubtitleActive', false);
|
|
const [logStatus, setLogStatus] = USE_STATE('logStatus', {
|
|
isModalLiveLogReady: false,
|
|
isFullLiveLogReady: false,
|
|
isDetailLiveLogReady: false,
|
|
isModalVodLogReady: false,
|
|
isFullVodLogReady: false,
|
|
isDetailVodLogReady: false,
|
|
isModalMediaLogReady: false,
|
|
isFullMediaLogReady: false,
|
|
isDetailMediaReady: false,
|
|
});
|
|
const [isVODPaused, setIsVODPaused] = USE_STATE('isVODPaused', false);
|
|
|
|
const panels = USE_SELECTOR('panels', (state) => state.panels.panels);
|
|
const chatData = USE_SELECTOR('chatData', (state) => state.play.chatData);
|
|
const shouldHideOverlays = USE_SELECTOR(
|
|
'shouldHideOverlays',
|
|
(state) => state.videoPlay.shouldHideOverlays
|
|
);
|
|
const shouldShowOverlays = USE_SELECTOR(
|
|
'shouldShowOverlays',
|
|
(state) => state.videoPlay.shouldShowOverlays
|
|
);
|
|
|
|
const popupVisible = USE_SELECTOR('popupVisible', (state) => state.common.popup.popupVisible);
|
|
const activePopup = USE_SELECTOR('activePopup', (state) => state.common.popup.activePopup);
|
|
const showDetailInfo = USE_SELECTOR('showDetailInfo', (state) => state.main.showDetailInfo);
|
|
const productImageLength = USE_SELECTOR(
|
|
'productImageLength',
|
|
(state) => state.product.productImageLength
|
|
);
|
|
const themeProductInfos = USE_SELECTOR(
|
|
'themeProductInfos',
|
|
(state) => state.home.themeCurationDetailInfoData
|
|
);
|
|
const hotelInfos = USE_SELECTOR(
|
|
'hotelInfos',
|
|
(state) => state.home.themeCurationHotelDetailData
|
|
);
|
|
const captionEnable = USE_SELECTOR(
|
|
'captionEnable',
|
|
(state) => state.common.appStatus.captionEnable
|
|
);
|
|
const fullVideolgCatCd = USE_SELECTOR(
|
|
'fullVideolgCatCd',
|
|
(state) => state.main.fullVideolgCatCd
|
|
);
|
|
const featuredShowsInfos = USE_SELECTOR(
|
|
'featuredShowsInfos',
|
|
(state) => state.main.featuredShowsInfos
|
|
);
|
|
const localRecentItems = USE_SELECTOR(
|
|
'localRecentItems',
|
|
(state) => state.localSettings?.recentItems
|
|
);
|
|
const httpHeader = USE_SELECTOR('httpHeader', (state) => state.common?.httpHeader);
|
|
const countryCode = USE_SELECTOR('countryCode', (state) => state.common.httpHeader?.cntry_cd);
|
|
const liveChannelInfos = USE_SELECTOR(
|
|
'liveChannelInfos',
|
|
(state) => state.main.liveChannelInfos
|
|
);
|
|
const showNowInfos = USE_SELECTOR('showNowInfos', (state) => state.main.showNowInfo);
|
|
const liveShowInfos = USE_SELECTOR('liveShowInfos', (state) => state.main.liveShowInfos);
|
|
const vodSubtitleData = USE_SELECTOR('vodSubtitleData', (state) => state.play.subTitleBlobs);
|
|
const broadcast = USE_SELECTOR('broadcast', (state) => state.common.broadcast);
|
|
const videoPlayState = USE_SELECTOR('videoPlayState', (state) => state.play.videoPlayState);
|
|
|
|
const lastPanelAction = USE_SELECTOR(
|
|
'lastPanelAction',
|
|
(state) => state.panels.lastPanelAction
|
|
);
|
|
const nowMenu = USE_SELECTOR('nowMenu', (state) => state.common.menu.nowMenu);
|
|
const nowMenuRef = usePrevious(nowMenu);
|
|
const entryMenu = USE_SELECTOR('entryMenu', (state) => state.common.menu.entryMenu);
|
|
const [videoLoaded, setVideoLoaded] = USE_STATE('videoLoaded', false);
|
|
const entryMenuRef = usePrevious(entryMenu);
|
|
const panelInfoRef = usePrevious(panelInfo);
|
|
|
|
const initialFocusTimeoutJob = useRef(new Job((func) => func(), 100));
|
|
const liveLogParamsRef = useRef(null);
|
|
const vodLogParamsRef = useRef(null);
|
|
const mediaLogParamsRef = useRef(null);
|
|
const prevNowMenuRef = useRef(null);
|
|
const watchIntervalLive = useRef(null);
|
|
const watchIntervalVod = useRef(null);
|
|
const watchIntervalMedia = useRef(null);
|
|
// useEffect(() => {
|
|
// console.log("###videoLoaded", videoLoaded);
|
|
// if (nowMenu) {
|
|
// }
|
|
// }, [videoLoaded]);
|
|
const currentLiveShowInfo = useMemo(() => {
|
|
if (liveShowInfos && liveShowInfos.length > 0) {
|
|
const panelInfoChanId = panelInfo?.chanId;
|
|
const isLive = panelInfo?.shptmBanrTpNm === 'LIVE';
|
|
|
|
if (isLive) {
|
|
const liveShowInfo = liveShowInfos //
|
|
.find(({ chanId }) => panelInfoChanId === chanId);
|
|
|
|
return liveShowInfo;
|
|
}
|
|
|
|
return {};
|
|
}
|
|
|
|
return {};
|
|
}, [liveShowInfos, panelInfo?.chanId, panelInfo?.shptmBanrTpNm]);
|
|
|
|
const currentVODShowInfo = useMemo(() => {
|
|
if (showDetailInfo && showDetailInfo.length > 0) {
|
|
const isVOD = panelInfo?.shptmBanrTpNm === 'VOD';
|
|
|
|
if (isVOD) {
|
|
const vodShowInfo = showDetailInfo[0];
|
|
|
|
return vodShowInfo;
|
|
}
|
|
|
|
return {};
|
|
}
|
|
|
|
return {};
|
|
}, [panelInfo?.shptmBanrTpNm, showDetailInfo]);
|
|
|
|
useEffect(() => {
|
|
if (!panelInfo?.modal && panelInfo?.shptmBanrTpNm === 'MEDIA') {
|
|
dispatch(sendLogGNB(Config.LOG_MENU.FULL));
|
|
prevNowMenuRef.current = nowMenuRef.current;
|
|
|
|
return () => dispatch(sendLogGNB(prevNowMenuRef.current));
|
|
} else if (panelInfo?.modal) {
|
|
dispatch(sendLogGNB(entryMenu));
|
|
}
|
|
}, [panelInfo?.modal, panelInfo?.shptmBanrTpNm]);
|
|
|
|
// useEffect(()=>{
|
|
// console.log('[MediaPanel] isOnTop:', {
|
|
// isOnTop,
|
|
// panelInfo
|
|
// });
|
|
// if (panelInfo && panelInfo.modal) {
|
|
// if (!isOnTop) {
|
|
// console.log('[MediaPanel] Not on top - pausing video');
|
|
// dispatch(pauseModalMedia());
|
|
// } else if (isOnTop && panelInfo.isPaused) {
|
|
// console.log('[MediaPanel] Back on top - resuming video');
|
|
// dispatch(resumeModalMedia());
|
|
// }
|
|
// }
|
|
// },[isOnTop, panelInfo])
|
|
|
|
// MediaPanel.jsx의 라인 313-327 useEffect 수정
|
|
// MEDIA 타입은 isOnTop 체크 하지 않음 (패널 구조 복잡도 때문에)
|
|
useEffect(() => {
|
|
console.log('[MediaPanel] isOnTop:', {
|
|
isOnTop,
|
|
panelInfo,
|
|
});
|
|
|
|
if (panelInfo && panelInfo.modal && panelInfo?.shptmBanrTpNm !== 'MEDIA') {
|
|
if (!isOnTop) {
|
|
console.log('[MediaPanel] Not on top - pausing video');
|
|
dispatch(pauseModalMedia());
|
|
} else if (isOnTop && panelInfo.isPaused) {
|
|
console.log('[MediaPanel] Back on top - resuming video');
|
|
dispatch(resumeModalMedia());
|
|
}
|
|
}
|
|
}, [isOnTop, panelInfo]);
|
|
|
|
// 새로운 useEffect 추가 (라인 328 이후)
|
|
useEffect(() => {
|
|
// modal 여부와 관계없이 videoPlayer가 있고 isPaused 값이 명시적일 때 제어
|
|
if (videoPlayer.current && panelInfo?.isPaused !== undefined) {
|
|
if (panelInfo.isPaused === true) {
|
|
videoPlayer.current.pause();
|
|
} else if (panelInfo.isPaused === false) {
|
|
videoPlayer.current.play();
|
|
}
|
|
}
|
|
}, [panelInfo?.isPaused]);
|
|
|
|
// creating live log params
|
|
useEffect(() => {
|
|
if (currentLiveShowInfo && Object.keys(currentLiveShowInfo).length > 0) {
|
|
if (currentLiveShowInfo.showId !== panelInfo?.showId) {
|
|
dispatch(
|
|
updatePanel({
|
|
name: panel_names.MEDIA_PANEL,
|
|
panelInfo: {
|
|
chanId: currentLiveShowInfo.chanId,
|
|
modalContainerId: panelInfo?.modalContainerId,
|
|
patnrId: currentLiveShowInfo.patnrId,
|
|
showId: currentLiveShowInfo.showId,
|
|
showUrl: currentLiveShowInfo.showUrl,
|
|
},
|
|
})
|
|
);
|
|
|
|
return;
|
|
}
|
|
|
|
let logTemplateNo;
|
|
|
|
if (isOnTop && panelInfo?.modal) {
|
|
logTemplateNo = getLogTpNo('LIVE', nowMenu);
|
|
setLogStatus((status) => ({ ...status, isModalLiveLogReady: true }));
|
|
}
|
|
//
|
|
else if (isOnTop && !panelInfo?.modal) {
|
|
logTemplateNo = Config.LOG_TP_NO.LIVE.FULL;
|
|
setLogStatus((status) => ({ ...status, isFullLiveLogReady: true }));
|
|
}
|
|
//
|
|
else if (!isOnTop && !panelInfo?.modal) {
|
|
logTemplateNo = Config.LOG_TP_NO.LIVE.ITEM_DETAIL;
|
|
setLogStatus((status) => ({ ...status, isDetailLiveLogReady: true }));
|
|
}
|
|
liveLogParamsRef.current = {
|
|
entryMenu: entryMenuRef.current,
|
|
lgCatCd: currentLiveShowInfo.catCd ?? '',
|
|
lgCatNm: currentLiveShowInfo.catNm ?? '',
|
|
linkTpCd: panelInfo?.linkTpCd ?? '',
|
|
logTpNo: logTemplateNo,
|
|
nowMenu: nowMenuRef.current,
|
|
patncNm: currentLiveShowInfo.patncNm,
|
|
patnrId: currentLiveShowInfo.patnrId,
|
|
showId: currentLiveShowInfo.showId,
|
|
showNm: currentLiveShowInfo.showNm,
|
|
vdoTpNm: currentLiveShowInfo.vtctpYn
|
|
? currentLiveShowInfo.vtctpYn === 'Y'
|
|
? 'Vertical'
|
|
: 'Horizontal'
|
|
: '',
|
|
};
|
|
}
|
|
}, [
|
|
currentLiveShowInfo,
|
|
isOnTop,
|
|
nowMenu,
|
|
panelInfo?.linkTpCd,
|
|
panelInfo?.modal,
|
|
panelInfo?.modalContainerId,
|
|
panelInfo?.showId,
|
|
setLogStatus,
|
|
]);
|
|
|
|
// send live log
|
|
useEffect(() => {
|
|
if (broadcast && Object.keys(broadcast).length === 0) {
|
|
// case: live, modal
|
|
if (
|
|
logStatus.isModalLiveLogReady &&
|
|
isOnTop &&
|
|
panelInfo?.modal &&
|
|
liveLogParamsRef.current?.showId === panelInfo?.showId
|
|
) {
|
|
let watchStrtDt = formatGMTString(new Date());
|
|
|
|
watchIntervalLive.current = setInterval(() => {
|
|
let watchEndDt = formatGMTString(new Date());
|
|
let watchRecord = {
|
|
...liveLogParamsRef.current,
|
|
watchStrtDt,
|
|
watchEndDt,
|
|
};
|
|
dispatch(changeLocalSettings({ watchRecord }));
|
|
}, 10000);
|
|
|
|
return () => {
|
|
setLogStatus((status) => ({
|
|
...status,
|
|
isModalLiveLogReady: false,
|
|
}));
|
|
clearInterval(watchIntervalLive.current);
|
|
dispatch(
|
|
sendLogLive({ ...liveLogParamsRef.current, watchStrtDt }, () =>
|
|
dispatch(changeLocalSettings({ watchRecord: {} }))
|
|
)
|
|
);
|
|
};
|
|
}
|
|
|
|
// case: live, full
|
|
if (
|
|
logStatus.isFullLiveLogReady &&
|
|
isOnTop &&
|
|
!panelInfo?.modal &&
|
|
liveLogParamsRef.current?.showId === panelInfo?.showId
|
|
) {
|
|
let watchStrtDt = formatGMTString(new Date());
|
|
|
|
watchIntervalLive.current = setInterval(() => {
|
|
let watchEndDt = formatGMTString(new Date());
|
|
let watchRecord = {
|
|
...liveLogParamsRef.current,
|
|
watchStrtDt,
|
|
watchEndDt,
|
|
};
|
|
dispatch(changeLocalSettings({ watchRecord }));
|
|
}, 10000);
|
|
|
|
return () => {
|
|
setLogStatus((status) => ({
|
|
...status,
|
|
isFullLiveLogReady: false,
|
|
}));
|
|
clearInterval(watchIntervalLive.current);
|
|
dispatch(
|
|
sendLogLive({ ...liveLogParamsRef.current, watchStrtDt }, () =>
|
|
dispatch(changeLocalSettings({ watchRecord: {} }))
|
|
)
|
|
);
|
|
};
|
|
}
|
|
|
|
// case: live, item detail
|
|
if (
|
|
logStatus.isDetailLiveLogReady &&
|
|
!isOnTop &&
|
|
!panelInfo?.modal &&
|
|
liveLogParamsRef.current?.showId === panelInfo?.showId
|
|
) {
|
|
let watchStrtDt = formatGMTString(new Date());
|
|
|
|
watchIntervalLive.current = setInterval(() => {
|
|
let watchEndDt = formatGMTString(new Date());
|
|
let watchRecord = {
|
|
...liveLogParamsRef.current,
|
|
watchStrtDt,
|
|
watchEndDt,
|
|
};
|
|
dispatch(changeLocalSettings({ watchRecord }));
|
|
}, 10000);
|
|
|
|
return () => {
|
|
setLogStatus((status) => ({
|
|
...status,
|
|
isDetailLiveLogReady: false,
|
|
}));
|
|
clearInterval(watchIntervalLive.current);
|
|
dispatch(
|
|
sendLogLive({ ...liveLogParamsRef.current, watchStrtDt }, () =>
|
|
dispatch(changeLocalSettings({ watchRecord: {} }))
|
|
)
|
|
);
|
|
};
|
|
}
|
|
}
|
|
}, [
|
|
broadcast,
|
|
isOnTop,
|
|
logStatus.isDetailLiveLogReady,
|
|
logStatus.isFullLiveLogReady,
|
|
logStatus.isModalLiveLogReady,
|
|
panelInfo?.modal,
|
|
panelInfo?.showId,
|
|
setLogStatus,
|
|
]);
|
|
|
|
// creating VOD log params
|
|
useEffect(() => {
|
|
if (currentVODShowInfo && Object.keys(currentVODShowInfo).length > 0 && !isVODPaused) {
|
|
let logTemplateNo;
|
|
|
|
if (isOnTop && panelInfo?.modal) {
|
|
logTemplateNo = getLogTpNo('VOD', nowMenu);
|
|
setLogStatus((status) => ({ ...status, isModalVodLogReady: true }));
|
|
}
|
|
//
|
|
else if (isOnTop && !panelInfo?.modal) {
|
|
logTemplateNo = Config.LOG_TP_NO.VOD.FULL_VOD;
|
|
setLogStatus((status) => ({ ...status, isFullVodLogReady: true }));
|
|
}
|
|
//
|
|
else if (!isOnTop && !panelInfo?.modal) {
|
|
logTemplateNo = Config.LOG_TP_NO.VOD.ITEM_DETAIL_VOD;
|
|
setLogStatus((status) => ({ ...status, isDetailVodLogReady: true }));
|
|
}
|
|
|
|
vodLogParamsRef.current = {
|
|
entryMenu: entryMenuRef.current,
|
|
lgCatCd: currentVODShowInfo?.showCatCd ?? '',
|
|
lgCatNm: currentVODShowInfo?.showCatNm ?? '',
|
|
logTpNo: logTemplateNo,
|
|
linkTpCd: panelInfo?.linkTpCd ?? '',
|
|
nowMenu: nowMenuRef.current,
|
|
patncNm: currentVODShowInfo?.patncNm,
|
|
patnrId: currentVODShowInfo?.patnrId,
|
|
showId: currentVODShowInfo?.showId,
|
|
showNm: currentVODShowInfo?.showNm,
|
|
vdoTpNm: currentVODShowInfo?.vtctpYn
|
|
? currentVODShowInfo.vtctpYn === 'Y'
|
|
? 'Vertical'
|
|
: 'Horizontal'
|
|
: '',
|
|
};
|
|
}
|
|
}, [currentVODShowInfo, isOnTop, isVODPaused, nowMenu, panelInfo?.linkTpCd, panelInfo?.modal]);
|
|
|
|
// send VOD log
|
|
useEffect(() => {
|
|
if (broadcast && Object.keys(broadcast).length === 0 && !isVODPaused) {
|
|
// case: VOD, modal
|
|
if (
|
|
logStatus.isModalVodLogReady &&
|
|
isOnTop &&
|
|
panelInfo?.modal &&
|
|
vodLogParamsRef.current?.showId === panelInfo?.showId
|
|
) {
|
|
let watchStrtDt = formatGMTString(new Date());
|
|
|
|
watchIntervalVod.current = setInterval(() => {
|
|
let watchEndDt = formatGMTString(new Date());
|
|
let watchRecord = {
|
|
...vodLogParamsRef.current,
|
|
watchStrtDt,
|
|
watchEndDt,
|
|
};
|
|
dispatch(changeLocalSettings({ watchRecord }));
|
|
}, 10000);
|
|
|
|
return () => {
|
|
setLogStatus((status) => ({
|
|
...status,
|
|
isModalVodLogReady: false,
|
|
}));
|
|
clearInterval(watchIntervalVod.current);
|
|
dispatch(
|
|
sendLogVOD({ ...vodLogParamsRef.current, watchStrtDt }, () =>
|
|
dispatch(changeLocalSettings({ watchRecord: {} }))
|
|
)
|
|
);
|
|
};
|
|
}
|
|
|
|
// case: VOD, full
|
|
if (
|
|
logStatus.isFullVodLogReady &&
|
|
isOnTop &&
|
|
!panelInfo?.modal &&
|
|
vodLogParamsRef.current?.showId === panelInfo?.showId
|
|
) {
|
|
let watchStrtDt = formatGMTString(new Date());
|
|
|
|
watchIntervalVod.current = setInterval(() => {
|
|
let watchEndDt = formatGMTString(new Date());
|
|
let watchRecord = {
|
|
...vodLogParamsRef.current,
|
|
watchStrtDt,
|
|
watchEndDt,
|
|
};
|
|
dispatch(changeLocalSettings({ watchRecord }));
|
|
}, 10000);
|
|
|
|
return () => {
|
|
setLogStatus((status) => ({
|
|
...status,
|
|
isFullVodLogReady: false,
|
|
}));
|
|
clearInterval(watchIntervalVod.current);
|
|
dispatch(
|
|
sendLogVOD({ ...vodLogParamsRef.current, watchStrtDt }, () =>
|
|
dispatch(changeLocalSettings({ watchRecord: {} }))
|
|
)
|
|
);
|
|
};
|
|
}
|
|
|
|
// case: VOD, item detail
|
|
if (
|
|
logStatus.isDetailVodLogReady &&
|
|
!isOnTop &&
|
|
!panelInfo?.modal &&
|
|
vodLogParamsRef.current?.showId === panelInfo?.showId
|
|
) {
|
|
let watchStrtDt = formatGMTString(new Date());
|
|
|
|
watchIntervalVod.current = setInterval(() => {
|
|
let watchEndDt = formatGMTString(new Date());
|
|
let watchRecord = {
|
|
...vodLogParamsRef.current,
|
|
watchStrtDt,
|
|
watchEndDt,
|
|
};
|
|
dispatch(changeLocalSettings({ watchRecord }));
|
|
}, 10000);
|
|
|
|
return () => {
|
|
setLogStatus((status) => ({
|
|
...status,
|
|
isDetailVodLogReady: false,
|
|
}));
|
|
clearInterval(watchIntervalVod.current);
|
|
dispatch(
|
|
sendLogVOD({ ...vodLogParamsRef.current, watchStrtDt }, () =>
|
|
dispatch(changeLocalSettings({ watchRecord: {} }))
|
|
)
|
|
);
|
|
};
|
|
}
|
|
}
|
|
}, [
|
|
broadcast,
|
|
isOnTop,
|
|
isVODPaused,
|
|
logStatus.isDetailVodLogReady,
|
|
logStatus.isFullVodLogReady,
|
|
logStatus.isModalVodLogReady,
|
|
panelInfo?.modal,
|
|
panelInfo?.showId,
|
|
setLogStatus,
|
|
]);
|
|
|
|
// creating media log params
|
|
useEffect(() => {
|
|
if (panelInfo?.shptmBanrTpNm === 'MEDIA' && isOnTop && !isVODPaused) {
|
|
let logTemplateNo;
|
|
|
|
if (panelInfo?.modal) {
|
|
logTemplateNo = getLogTpNo('MEDIA', nowMenu);
|
|
setLogStatus((status) => ({ ...status, isModalMediaLogReady: true }));
|
|
}
|
|
//
|
|
else if (!panelInfo?.modal) {
|
|
logTemplateNo = Config.LOG_TP_NO.VOD.FULL_MEDIA;
|
|
setLogStatus((status) => ({ ...status, isFullMediaLogReady: true }));
|
|
}
|
|
|
|
mediaLogParamsRef.current = {
|
|
entryMenu: entryMenuRef.current,
|
|
lgCatCd: panelInfo?.lgCatCd ?? '',
|
|
lgCatNm: panelInfo?.lgCatNm ?? '',
|
|
logTpNo: logTemplateNo,
|
|
linkTpCd: panelInfo?.linkTpCd ?? '',
|
|
nowMenu: nowMenuRef.current,
|
|
patncNm: panelInfo?.patncNm ?? '',
|
|
patnrId: panelInfo?.patnrId ?? '',
|
|
showId: panelInfo?.prdtId ?? panelInfo?.showId,
|
|
showNm: panelInfo?.prdtNm ?? panelInfo?.showNm,
|
|
vdoTpNm: 'Horizontal',
|
|
};
|
|
}
|
|
}, [
|
|
isOnTop,
|
|
isVODPaused,
|
|
nowMenu,
|
|
panelInfo?.lgCatCd,
|
|
panelInfo?.lgCatNm,
|
|
panelInfo?.linkTpCd,
|
|
panelInfo?.modal,
|
|
panelInfo?.patncNm,
|
|
panelInfo?.patnrId,
|
|
panelInfo?.prdtId,
|
|
panelInfo?.prdtNm,
|
|
panelInfo?.showId,
|
|
panelInfo?.showNm,
|
|
panelInfo?.shptmBanrTpNm,
|
|
setLogStatus,
|
|
]);
|
|
|
|
// send log media
|
|
useEffect(() => {
|
|
if (broadcast && Object.keys(broadcast).length === 0 && !isVODPaused) {
|
|
// case: media, modal
|
|
if (logStatus.isModalMediaLogReady && panelInfo?.modal) {
|
|
let watchStrtDt = formatGMTString(new Date());
|
|
|
|
watchIntervalMedia.current = setInterval(() => {
|
|
let watchEndDt = formatGMTString(new Date());
|
|
let watchRecord = {
|
|
...mediaLogParamsRef.current,
|
|
watchStrtDt,
|
|
watchEndDt,
|
|
};
|
|
dispatch(changeLocalSettings({ watchRecord }));
|
|
}, 10000);
|
|
|
|
return () => {
|
|
setLogStatus((status) => ({
|
|
...status,
|
|
isModalMediaLogReady: false,
|
|
}));
|
|
clearInterval(watchIntervalMedia.current);
|
|
dispatch(
|
|
sendLogVOD({ ...mediaLogParamsRef.current, watchStrtDt }, () =>
|
|
dispatch(changeLocalSettings({ watchRecord: {} }))
|
|
)
|
|
);
|
|
};
|
|
}
|
|
}
|
|
|
|
// case: media, full
|
|
if (logStatus.isFullMediaLogReady && !panelInfo?.modal) {
|
|
let watchStrtDt = formatGMTString(new Date());
|
|
|
|
watchIntervalMedia.current = setInterval(() => {
|
|
let watchEndDt = formatGMTString(new Date());
|
|
let watchRecord = {
|
|
...mediaLogParamsRef.current,
|
|
watchStrtDt,
|
|
watchEndDt,
|
|
};
|
|
dispatch(changeLocalSettings({ watchRecord }));
|
|
}, 10000);
|
|
|
|
return () => {
|
|
setLogStatus((status) => ({
|
|
...status,
|
|
isFullMediaLogReady: false,
|
|
}));
|
|
clearInterval(watchIntervalMedia.current);
|
|
dispatch(
|
|
sendLogVOD({ ...mediaLogParamsRef.current, watchStrtDt }, () =>
|
|
dispatch(changeLocalSettings({ watchRecord: {} }))
|
|
)
|
|
);
|
|
};
|
|
}
|
|
}, [
|
|
broadcast,
|
|
isVODPaused,
|
|
logStatus.isFullMediaLogReady,
|
|
logStatus.isModalMediaLogReady,
|
|
panelInfo?.modal,
|
|
setLogStatus,
|
|
]);
|
|
|
|
const videoVerticalVisible = useMemo(() => {
|
|
if (playListInfo && playListInfo[selectedIndex]?.vtctpYn === 'Y') {
|
|
return true;
|
|
}
|
|
return false;
|
|
}, [playListInfo, selectedIndex]);
|
|
|
|
const handleItemFocus = useCallback(
|
|
(menu) => {
|
|
dispatch(sendLogGNB(menu));
|
|
},
|
|
[dispatch]
|
|
);
|
|
|
|
const handleClickBack = useCallback(
|
|
(ev, isEnd) => {
|
|
//modal로부터 Full 전환된 경우 다시 preview 모드로 돌아감.
|
|
if (panelInfo.modalContainerId && !panelInfo.modal) {
|
|
dispatch(
|
|
startMediaPlayer({
|
|
...panelInfo,
|
|
modal: true,
|
|
modalClassName: '',
|
|
})
|
|
);
|
|
videoPlayer.current?.hideControls();
|
|
setSelectedIndex(backupInitialIndex);
|
|
if (panelInfo.shptmBanrTpNm === 'MEDIA') {
|
|
dispatch(
|
|
updatePanel({
|
|
name: panel_names.DETAIL_PANEL,
|
|
panelInfo: {
|
|
launchedFromPlayer: false,
|
|
},
|
|
})
|
|
);
|
|
}
|
|
|
|
// 모달 복귀 시 상태만 업데이트 - 포커스는 자연스럽게 ProductVideo로
|
|
console.log('[MediaPanel] Back button pressed - returning to modal mode');
|
|
|
|
ev?.stopPropagation();
|
|
// ev?.preventDefault();
|
|
return;
|
|
}
|
|
|
|
if (!panelInfo.modal) {
|
|
dispatch(PanelActions.popPanel());
|
|
dispatch(changeAppStatus({ cursorVisible: false }));
|
|
document?.dispatchEvent?.(new CustomEvent('detailpanel-scroll-reset'));
|
|
|
|
//딮링크로 플레이어 진입 후 이전버튼 클릭시
|
|
if (panels.length === 1) {
|
|
setTimeout(() => {
|
|
Spotlight.focus(SpotlightIds.HOME_TBODY);
|
|
});
|
|
}
|
|
ev?.stopPropagation();
|
|
// ev?.preventDefault();
|
|
|
|
return;
|
|
}
|
|
},
|
|
[dispatch, panelInfo, videoPlayer, videoVerticalVisible, backupInitialIndex, panels]
|
|
);
|
|
|
|
useEffect(() => {
|
|
//todo if(modal)
|
|
return () => {
|
|
// 패널이 2개 존재할때만 popPanel 진행
|
|
// fullscreen 전환 중이면 popPanel하지 않음
|
|
if (panelInfo.modal && !isOnTop && !isTransitioningToFullscreen.current) {
|
|
dispatch(PanelActions.popPanel());
|
|
} else {
|
|
Spotlight.focus('tbody');
|
|
}
|
|
// fullscreen 전환 완료 후 플래그 초기화
|
|
isTransitioningToFullscreen.current = false;
|
|
};
|
|
}, [isOnTop]);
|
|
|
|
useEffect(() => {
|
|
if (showNowInfos && panelInfo.shptmBanrTpNm === 'LIVE') {
|
|
const period = showNowInfos.period !== undefined ? showNowInfos.period : 30;
|
|
const periodInMilliseconds = period * 1000;
|
|
|
|
const timer = setTimeout(() => {
|
|
dispatch(
|
|
getMainLiveShowNowProduct({
|
|
patnrId: panelInfo.patnrId ? panelInfo.patnrId : playListInfo[selectedIndex].patnrId,
|
|
showId: panelInfo.showId ? panelInfo.showId : playListInfo[selectedIndex].showId,
|
|
lstChgDt: showNowInfos.lstChgDt,
|
|
})
|
|
);
|
|
}, periodInMilliseconds);
|
|
|
|
return () => {
|
|
clearTimeout(timer);
|
|
};
|
|
}
|
|
}, [showNowInfos, panelInfo]);
|
|
|
|
const videoItemFocused = useCallback(() => {
|
|
// 아이템클릭 진입시 포커스
|
|
let hasProperSpot = false;
|
|
let targetId;
|
|
if (!isInitialFocusOccurred) {
|
|
targetId = panelInfo.targetId;
|
|
|
|
initialFocusTimeoutJob.current.start(() => {
|
|
const initialFocusTarget = findSelector(`[data-spotlight-id="${targetId}"]`);
|
|
|
|
if (initialFocusTarget) {
|
|
hasProperSpot = true;
|
|
Spotlight.focus(initialFocusTarget);
|
|
setIsInitialFocusOccurred(true);
|
|
|
|
return;
|
|
}
|
|
});
|
|
}
|
|
}, [isInitialFocusOccurred, panelInfo.targetId, panelInfo.modal, videoVerticalVisible]);
|
|
|
|
const videoInitialFocused = useCallback(() => {
|
|
if (panelInfo.isUpdatedByClick || !isOnTop) {
|
|
return;
|
|
}
|
|
setContainerLastFocusedElement(null, ['playVideoShopNowBox']);
|
|
|
|
// 세로형모드 포커스
|
|
if (videoVerticalVisible) {
|
|
Spotlight.focus('tab-0');
|
|
return;
|
|
}
|
|
|
|
// 화살표버튼 포커스
|
|
const current = Spotlight.getCurrent();
|
|
let hasProperSpot = false;
|
|
if (current) {
|
|
const spotId = current.getAttribute('data-spotlight-id');
|
|
if (spotId && spotId.indexOf('tabChannel-video') >= 0) {
|
|
hasProperSpot = true;
|
|
}
|
|
}
|
|
|
|
if (!panelInfo.modal && !videoVerticalVisible && !hasProperSpot) {
|
|
Spotlight.focus('below-tab-live-channel-button');
|
|
return;
|
|
}
|
|
//비디오 진입시 포커스
|
|
if (panelInfo.isIndicatorByClick && shopNowInfo?.length > 0) {
|
|
Spotlight.focus('playVideoShopNowBox');
|
|
return;
|
|
}
|
|
}, [
|
|
shopNowInfo,
|
|
videoVerticalVisible,
|
|
isOnTop,
|
|
panelInfo.modal,
|
|
panelInfo.isUpdatedByClick,
|
|
panelInfo.isIndicatorByClick,
|
|
panelInfo.shptmBanrTpNm,
|
|
]);
|
|
|
|
// 최상단 패널 정보 (여러 useMemo에서 공통으로 사용)
|
|
const topPanel = useMemo(() => {
|
|
return panels[panels.length - 1];
|
|
}, [panels]);
|
|
|
|
// 최상단 패널이 DetailPanel이고 MediaPanel에서 진입했는지 확인
|
|
const isTopPanelDetailFromPlayer = useMemo(() => {
|
|
return (
|
|
topPanel?.name === panel_names.DETAIL_PANEL &&
|
|
topPanel?.panelInfo?.launchedFromPlayer === true
|
|
);
|
|
}, [topPanel]);
|
|
|
|
const cannotPlay = useMemo(() => {
|
|
// URL이 있으면 항상 재생 가능 (isOnTop 조건 제거)
|
|
return false;
|
|
}, []);
|
|
|
|
const getPlayer = useCallback((ref) => {
|
|
videoPlayer.current = ref;
|
|
}, []);
|
|
|
|
const playVideo = useCallback(() => {
|
|
videoPlayer.current?.play?.();
|
|
}, []);
|
|
|
|
const pauseVideo = useCallback(() => {
|
|
videoPlayer.current?.pause?.();
|
|
}, []);
|
|
|
|
const seekVideo = useCallback((seconds = 0) => {
|
|
videoPlayer.current?.seek?.(seconds);
|
|
}, []);
|
|
|
|
const showControls = useCallback(() => {
|
|
videoPlayer.current?.showControls?.();
|
|
}, []);
|
|
|
|
const hideControls = useCallback(() => {
|
|
videoPlayer.current?.hideControls?.();
|
|
}, []);
|
|
|
|
const toggleControls = useCallback(() => {
|
|
videoPlayer.current?.toggleControls?.();
|
|
}, []);
|
|
|
|
const getCurrentMediaState = useCallback(() => {
|
|
return videoPlayer.current?.getMediaState?.();
|
|
}, []);
|
|
|
|
const openModal = useCallback(
|
|
(params = {}) => {
|
|
const {
|
|
modal = true,
|
|
modalContainerId = 'product-video-player',
|
|
modalClassName = '',
|
|
spotlightDisable = true,
|
|
...rest
|
|
} = params;
|
|
|
|
dispatch(
|
|
startMediaPlayer({
|
|
modal,
|
|
modalContainerId,
|
|
modalClassName,
|
|
spotlightDisable,
|
|
...rest,
|
|
})
|
|
);
|
|
},
|
|
[dispatch]
|
|
);
|
|
|
|
const close = useCallback(() => {
|
|
dispatch(finishModalMediaForce());
|
|
}, [dispatch]);
|
|
|
|
const enterFullscreen = useCallback(() => {
|
|
isTransitioningToFullscreen.current = true;
|
|
dispatch(switchMediaToFullscreen());
|
|
}, [dispatch]);
|
|
|
|
const enterModal = useCallback(
|
|
(modalContainerId = 'product-video-player', modalClassName = '') => {
|
|
dispatch(switchMediaToModal(modalContainerId, modalClassName));
|
|
},
|
|
[dispatch]
|
|
);
|
|
|
|
const minimizeViaRef = useCallback(() => {
|
|
dispatch(minimizeModalMedia());
|
|
}, [dispatch]);
|
|
|
|
const requestDetailScrollReset = useCallback(() => {
|
|
if (typeof document !== 'undefined') {
|
|
document.dispatchEvent(new CustomEvent('detailpanel-scroll-reset'));
|
|
}
|
|
}, []);
|
|
|
|
const canRestoreFromDetailPanel = useCallback(() => {
|
|
if (typeof window === 'undefined') return true;
|
|
return window.detailPanelScrollTop === 0;
|
|
}, []);
|
|
|
|
const restoreWhenScrollZero = useCallback(() => {
|
|
let attempts = 0;
|
|
const attemptRestore = () => {
|
|
if (canRestoreFromDetailPanel()) {
|
|
dispatch(restoreModalMedia());
|
|
} else if (attempts < 5) {
|
|
attempts += 1;
|
|
requestDetailScrollReset();
|
|
setTimeout(attemptRestore, 50);
|
|
}
|
|
};
|
|
attemptRestore();
|
|
}, [canRestoreFromDetailPanel, dispatch, requestDetailScrollReset]);
|
|
|
|
const restoreViaRef = useCallback(() => {
|
|
restoreWhenScrollZero();
|
|
}, [restoreWhenScrollZero]);
|
|
|
|
useImperativeHandle(
|
|
ref,
|
|
() => ({
|
|
playVideo,
|
|
pauseVideo,
|
|
seekVideo,
|
|
showControls,
|
|
hideControls,
|
|
toggleControls,
|
|
getCurrentMediaState,
|
|
minimize: minimizeViaRef,
|
|
restore: restoreViaRef,
|
|
openModal,
|
|
close,
|
|
enterFullscreen,
|
|
enterModal,
|
|
getInternalPlayer: () => videoPlayer.current,
|
|
}),
|
|
[
|
|
playVideo,
|
|
pauseVideo,
|
|
seekVideo,
|
|
showControls,
|
|
hideControls,
|
|
toggleControls,
|
|
getCurrentMediaState,
|
|
minimizeViaRef,
|
|
restoreViaRef,
|
|
openModal,
|
|
close,
|
|
enterFullscreen,
|
|
enterModal,
|
|
]
|
|
);
|
|
|
|
/** for VOD */
|
|
const addPanelInfoToPlayList = useCallback(
|
|
(featuredShowsInfos) => {
|
|
if (Array.isArray(featuredShowsInfos)) {
|
|
const showId = showDetailInfo[0]?.showId;
|
|
|
|
const index = featuredShowsInfos.findIndex((show) => show.showId === showId);
|
|
|
|
let newArray = [];
|
|
if (index > -1) {
|
|
// 인덱스를 찾은 경우 그대로
|
|
newArray = [...featuredShowsInfos];
|
|
setBackupInitialIndex(index);
|
|
setSelectedIndex(index);
|
|
} else {
|
|
// 인덱스를 찾지 못한 경우 showDetailInfo 를 제일 앞에 배치
|
|
newArray = [{ ...showDetailInfo[0] }, ...featuredShowsInfos];
|
|
setBackupInitialIndex(0);
|
|
setSelectedIndex(0);
|
|
}
|
|
setPlayListInfo(newArray);
|
|
}
|
|
},
|
|
|
|
[showDetailInfo]
|
|
);
|
|
|
|
useEffect(() => {
|
|
if (panelInfo.shptmBanrTpNm === 'LIVE') {
|
|
if (panelInfo.patnrId && panelInfo.showId) {
|
|
dispatch(
|
|
getMainCategoryShowDetail({
|
|
patnrId: panelInfo.patnrId,
|
|
showId: panelInfo.showId,
|
|
curationId: panelInfo.curationId,
|
|
})
|
|
);
|
|
}
|
|
dispatch(getMainLiveShow({ vodIncFlag: 'Y' }));
|
|
}
|
|
}, [
|
|
dispatch,
|
|
panelInfo?.curationId,
|
|
panelInfo?.lgCatCd,
|
|
panelInfo?.patnrId,
|
|
panelInfo?.showId,
|
|
panelInfo?.shptmBanrTpNm,
|
|
]);
|
|
|
|
useEffect(() => {
|
|
if (
|
|
panelInfo.shptmBanrTpNm === 'VOD' &&
|
|
showDetailInfo &&
|
|
showDetailInfo.length > 0 &&
|
|
showDetailInfo[0]?.showCatCd &&
|
|
fullVideolgCatCd !== showDetailInfo[0]?.showCatCd //기존에 호출했으면 안한다.
|
|
) {
|
|
dispatch(
|
|
getHomeFullVideoInfo({
|
|
lgCatCd: showDetailInfo[0].showCatCd,
|
|
})
|
|
);
|
|
}
|
|
if (
|
|
panelInfo.shptmBanrTpNm === 'VOD' &&
|
|
showDetailInfo &&
|
|
showDetailInfo[0] &&
|
|
showDetailInfo[0].showId &&
|
|
showDetailInfo[0].patnrId
|
|
) {
|
|
if (!featuredShowsInfos || Object.keys(featuredShowsInfos).length === 0) {
|
|
setPlayListInfo(showDetailInfo);
|
|
}
|
|
setShopNowInfo(showDetailInfo[0].productInfos);
|
|
saveToLocalSettings(showDetailInfo[0].showId, showDetailInfo[0].patnrId);
|
|
}
|
|
}, [showDetailInfo]);
|
|
|
|
//LIVE
|
|
useEffect(() => {
|
|
if (playListInfo && playListInfo.length > 0 && panelInfo.shptmBanrTpNm === 'LIVE') {
|
|
if (playListInfo[selectedIndex]?.patnrId) {
|
|
dispatch(
|
|
getMainLiveShowNowProduct({
|
|
patnrId: playListInfo[selectedIndex]?.patnrId,
|
|
showId: playListInfo[selectedIndex]?.showId,
|
|
})
|
|
);
|
|
}
|
|
|
|
if (playListInfo[selectedIndex]?.catCd) {
|
|
dispatch(
|
|
getHomeFullVideoInfo({
|
|
lgCatCd: playListInfo[selectedIndex]?.catCd,
|
|
})
|
|
);
|
|
}
|
|
}
|
|
}, [selectedIndex]);
|
|
|
|
useEffect(() => {
|
|
if (showDetailInfo && showDetailInfo.length > 0) {
|
|
dispatch(CLEAR_PLAYER_INFO());
|
|
|
|
if (
|
|
showDetailInfo[0]?.liveFlag === 'N' &&
|
|
showDetailInfo[0]?.chatLogFlag === 'Y' &&
|
|
panelInfo.shptmBanrTpNm === 'VOD'
|
|
) {
|
|
dispatch(getChatLog({ patnrId: panelInfo.patnrId, showId: panelInfo.showId }));
|
|
}
|
|
}
|
|
}, [showDetailInfo]);
|
|
|
|
// videoClick focused
|
|
useEffect(() => {
|
|
if (playListInfo && playListInfo.length > 0) {
|
|
if (panelInfo.targetId) {
|
|
videoItemFocused();
|
|
} else {
|
|
videoInitialFocused();
|
|
}
|
|
}
|
|
}, [playListInfo]);
|
|
|
|
//10초 후 닫힐때 TabButton 포커스
|
|
useEffect(() => {
|
|
if (playListInfo && playListInfo.length > 0) {
|
|
videoInitialFocused();
|
|
}
|
|
}, [panelInfo.modal]);
|
|
|
|
// liveChannel initial selectedIndex
|
|
useEffect(() => {
|
|
if (panelInfo?.shptmBanrTpNm === 'LIVE' && playListInfo?.length > 0) {
|
|
const index = playListInfo.findIndex((item) => item.chanId === panelInfo.chanId);
|
|
if (index !== -1 && !isUpdate) {
|
|
setBackupInitialIndex(index);
|
|
setSelectedIndex(index);
|
|
setIsUpdate(true);
|
|
}
|
|
}
|
|
}, [panelInfo?.shptmBanrTpNm, playListInfo]);
|
|
|
|
// live subtitle Luna API
|
|
useEffect(() => {
|
|
if (currentSubtitleBlob) {
|
|
return;
|
|
} else if (isYoutube) {
|
|
if (mediaId) {
|
|
dispatch(requestLiveSubtitle({ mediaId, enable: false }));
|
|
setMediaId(null);
|
|
}
|
|
return;
|
|
//do caption action on VideoPlayer(componentDidUpdate)
|
|
} else {
|
|
if (mediaId && captionEnable && isSubtitleActive && !panelInfo?.modal) {
|
|
dispatch(requestLiveSubtitle({ mediaId, enable: true }));
|
|
} else {
|
|
if (mediaId) {
|
|
dispatch(requestLiveSubtitle({ mediaId, enable: false }));
|
|
}
|
|
}
|
|
}
|
|
}, [
|
|
mediaId,
|
|
isYoutube,
|
|
captionEnable,
|
|
isSubtitleActive,
|
|
currentSubtitleBlob,
|
|
panelInfo.modal,
|
|
panelInfo.shptmBanrTpNm,
|
|
]);
|
|
|
|
// get PlayListInfo
|
|
useEffect(() => {
|
|
if (panelInfo?.shptmBanrTpNm === 'VOD') {
|
|
if (showDetailInfo && showDetailInfo.length > 0) {
|
|
if (featuredShowsInfos && featuredShowsInfos.length > 0) {
|
|
addPanelInfoToPlayList(featuredShowsInfos);
|
|
}
|
|
}
|
|
}
|
|
}, [featuredShowsInfos]);
|
|
|
|
// get PlayListInfo
|
|
useEffect(() => {
|
|
if (!panelInfo) return;
|
|
|
|
switch (panelInfo.shptmBanrTpNm) {
|
|
case 'LIVE': {
|
|
const playlist = liveShowInfos ?? liveChannelInfos;
|
|
|
|
if (!Array.isArray(playlist)) return;
|
|
|
|
const modifiedList = [];
|
|
|
|
playlist.forEach((item) => {
|
|
if (item.showType === 'vod' && Array.isArray(item.vodInfos)) {
|
|
const mergedVodInfos = item.vodInfos.map((vod) => ({
|
|
...vod,
|
|
patnrId: item.patnrId,
|
|
patncNm: item.patncNm,
|
|
patncLogoPath: item.patncLogoPath,
|
|
showType: 'vod',
|
|
}));
|
|
|
|
modifiedList.push(...mergedVodInfos);
|
|
} else {
|
|
modifiedList.push(item);
|
|
}
|
|
});
|
|
|
|
setPlayListInfo(modifiedList);
|
|
|
|
if (showNowInfos?.prdtChgYn === 'N') {
|
|
return;
|
|
}
|
|
|
|
if (showNowInfos || showDetailInfo?.length > 0) {
|
|
const productInfos = showNowInfos
|
|
? showNowInfos.productInfos
|
|
: showDetailInfo[0]?.productInfos;
|
|
setShopNowInfo(productInfos);
|
|
}
|
|
break;
|
|
}
|
|
case 'MEDIA':
|
|
setPlayListInfo([panelInfo]);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}, [
|
|
panelInfo,
|
|
showDetailInfo,
|
|
featuredShowsInfos,
|
|
liveChannelInfos,
|
|
liveShowInfos,
|
|
showNowInfos,
|
|
dispatch,
|
|
]);
|
|
|
|
const liveTotalTime = useMemo(() => {
|
|
let liveTotalTime;
|
|
if (liveShowInfos && panelInfo?.shptmBanrTpNm === 'LIVE') {
|
|
const startDtMoment = new Date(liveShowInfos[selectedIndex]?.strtDt);
|
|
const endDtMoment = new Date(liveShowInfos[selectedIndex]?.endDt);
|
|
|
|
liveTotalTime = Math.floor((endDtMoment - startDtMoment) / 1000);
|
|
|
|
return liveTotalTime;
|
|
}
|
|
}, [liveShowInfos, selectedIndex, panelInfo.shptmBanrTpNm]);
|
|
|
|
useEffect(() => {
|
|
const handleVisibilityChange = () => {
|
|
if (
|
|
document.visibilityState === 'visible' &&
|
|
liveShowInfos &&
|
|
panelInfo?.shptmBanrTpNm === 'LIVE'
|
|
) {
|
|
const localStartDt = convertUtcToLocal(liveShowInfos[selectedIndex]?.strtDt);
|
|
|
|
const curDt = new Date();
|
|
const localStartSec = localStartDt?.getTime() / 1000;
|
|
const curSec = curDt?.getTime() / 1000;
|
|
|
|
const calculatedLiveTime = curSec - localStartSec;
|
|
if (calculatedLiveTime >= liveTotalTime) {
|
|
setCurrentLiveTimeSeconds(0);
|
|
} else {
|
|
setCurrentLiveTimeSeconds(calculatedLiveTime);
|
|
}
|
|
}
|
|
};
|
|
|
|
document.addEventListener('visibilitychange', handleVisibilityChange);
|
|
|
|
if (panelInfo.offsetHour) {
|
|
setCurrentLiveTimeSeconds(parseInt(panelInfo.offsetHour));
|
|
} else if (liveShowInfos && panelInfo?.shptmBanrTpNm === 'LIVE') {
|
|
const localStartDt = convertUtcToLocal(liveShowInfos[selectedIndex]?.strtDt);
|
|
|
|
const curDt = new Date();
|
|
const localStartSec = localStartDt?.getTime() / 1000;
|
|
const curSec = curDt?.getTime() / 1000;
|
|
|
|
const calculatedLiveTime = curSec - localStartSec;
|
|
if (calculatedLiveTime >= liveTotalTime) {
|
|
setCurrentLiveTimeSeconds(0);
|
|
} else {
|
|
setCurrentLiveTimeSeconds(calculatedLiveTime);
|
|
}
|
|
} else {
|
|
setCurrentLiveTimeSeconds(0);
|
|
}
|
|
|
|
return () => {
|
|
document.removeEventListener('visibilitychange', handleVisibilityChange);
|
|
};
|
|
}, [
|
|
liveShowInfos,
|
|
selectedIndex,
|
|
panelInfo.offsetHour,
|
|
panelInfo.shptmBanrTpNm,
|
|
playListInfo,
|
|
liveTotalTime,
|
|
]);
|
|
|
|
useEffect(() => {
|
|
if (panelInfo.shptmBanrTpNm == 'LIVE' && liveTotalTime > 0) {
|
|
const interval = setInterval(() => {
|
|
setCurrentLiveTimeSeconds((prev) => {
|
|
if (prev >= liveTotalTime) {
|
|
return 1;
|
|
}
|
|
return prev + 1;
|
|
});
|
|
}, 1000);
|
|
|
|
return () => clearInterval(interval);
|
|
}
|
|
}, [liveTotalTime]);
|
|
|
|
useEffect(() => {
|
|
if (currentLiveTimeSeconds > liveTotalTime) {
|
|
setTimeout(() => {
|
|
dispatch(getMainLiveShow());
|
|
setShopNowInfo('');
|
|
dispatch(
|
|
getHomeFullVideoInfo({
|
|
lgCatCd: playListInfo[selectedIndex].showCatCd,
|
|
})
|
|
);
|
|
}, 3000);
|
|
}
|
|
}, [currentLiveTimeSeconds, liveTotalTime]);
|
|
|
|
const mediainfoHandler = useCallback(
|
|
(ev) => {
|
|
const type = ev.type;
|
|
if (type !== 'timeupdate' && type !== 'durationchange') {
|
|
console.log('mediainfoHandler....', type, ev, videoPlayer.current?.getMediaState());
|
|
}
|
|
if (ev === 'hlsError' && isNaN(Number(videoPlayer.current?.getMediaState().playbackRate))) {
|
|
dispatch(
|
|
sendBroadCast({
|
|
type: 'videoError',
|
|
moreInfo: { reason: 'hlsError' },
|
|
})
|
|
);
|
|
|
|
return;
|
|
}
|
|
|
|
switch (type) {
|
|
case 'timeupdate': {
|
|
setCurrentTime(videoPlayer.current?.getMediaState()?.currentTime);
|
|
break;
|
|
}
|
|
case 'error': {
|
|
dispatch(
|
|
sendBroadCast({
|
|
type: 'videoError',
|
|
moreInfo: { reason: videoPlayer.current?.getMediaState().error },
|
|
})
|
|
);
|
|
break;
|
|
}
|
|
case 'loadeddata': {
|
|
const mediaId = videoPlayer.current?.video?.media?.mediaId;
|
|
setMediaId(mediaId);
|
|
setVideoLoaded(true);
|
|
}
|
|
}
|
|
},
|
|
[dispatch]
|
|
);
|
|
|
|
useEffect(() => {
|
|
// case: video error when the video is in fullscreen mode
|
|
if (
|
|
broadcast?.type === 'videoError' &&
|
|
isOnTop &&
|
|
!panelInfo?.modal &&
|
|
panelInfo?.modalContainerId
|
|
) {
|
|
// case: Featured Brands
|
|
if (panelInfo?.sourcePanel === panel_names.FEATURED_BRANDS_PANEL) {
|
|
dispatch(PanelActions.popPanel());
|
|
}
|
|
}
|
|
}, [
|
|
broadcast?.type,
|
|
isOnTop,
|
|
panelInfo?.modal,
|
|
panelInfo?.modalContainerId,
|
|
panelInfo?.sourcePanel,
|
|
]);
|
|
|
|
useEffect(() => {
|
|
// 복구 직후: skipModalStyleRecalculation이 true면 저장된 modalStyle 적용
|
|
if (panelInfo.skipModalStyleRecalculation && !panelInfo.shouldShrinkTo1px) {
|
|
console.log('[MediaPanel] Condition 2.5: Using saved modalStyle from expand');
|
|
const shrinkInfo = panelInfo.playerState?.shrinkInfo;
|
|
|
|
// 저장된 modalStyle 사용 (top, left 포함)
|
|
if (shrinkInfo?.modalStyle) {
|
|
setModalStyle(normalizeModalStyle(shrinkInfo.modalStyle));
|
|
setModalScale(panelInfo.modalScale || shrinkInfo.modalScale);
|
|
} else {
|
|
setModalStyle(normalizeModalStyle(panelInfo.modalStyle));
|
|
setModalScale(panelInfo.modalScale);
|
|
}
|
|
|
|
// DOM 렌더링 후 플래그 제거
|
|
if (typeof window !== 'undefined') {
|
|
window.requestAnimationFrame(() => {
|
|
window.requestAnimationFrame(() => {
|
|
console.log('[MediaPanel] Condition 2.5: Removing skipFlag after DOM render');
|
|
dispatch(
|
|
updatePanel({
|
|
name: panel_names.MEDIA_PANEL,
|
|
panelInfo: {
|
|
...panelInfo,
|
|
skipModalStyleRecalculation: false,
|
|
},
|
|
})
|
|
);
|
|
});
|
|
});
|
|
}
|
|
}
|
|
|
|
if (
|
|
panelInfo.modal &&
|
|
!panelInfo.shouldShrinkTo1px &&
|
|
panelInfo.modalContainerId &&
|
|
(lastPanelAction === 'previewPush' || lastPanelAction === 'previewUpdate')
|
|
) {
|
|
console.log('[MediaPanel] Condition 1: Calculating modalStyle from DOM', {
|
|
lastPanelAction,
|
|
});
|
|
const node = document.querySelector(`[data-spotlight-id="${panelInfo.modalContainerId}"]`);
|
|
if (node) {
|
|
const { width, height, top, left } = node.getBoundingClientRect();
|
|
const baseModalStyle = {
|
|
width: `${width}px`,
|
|
height: `${height}px`,
|
|
top: `${top}px`,
|
|
left: `${left}px`,
|
|
position: 'fixed',
|
|
overflow: 'visible',
|
|
};
|
|
const normalizedStyle = normalizeModalStyle(baseModalStyle);
|
|
setModalStyle(normalizedStyle);
|
|
let scale = 1;
|
|
if (typeof window === 'object') {
|
|
scale = width / window.innerWidth;
|
|
setModalScale(scale);
|
|
}
|
|
dispatch(
|
|
updatePanel({
|
|
name: panel_names.MEDIA_PANEL,
|
|
panelInfo: {
|
|
modalStyle: normalizedStyle,
|
|
modalScale: scale,
|
|
},
|
|
})
|
|
);
|
|
} else {
|
|
console.log('[MediaPanel] Condition 1: Node not found, using saved modalStyle');
|
|
setModalStyle(normalizeModalStyle(panelInfo.modalStyle));
|
|
setModalScale(panelInfo.modalScale);
|
|
}
|
|
} else if (panelInfo.shouldShrinkTo1px) {
|
|
console.log('[MediaPanel] Condition 2: Shrinking - clearing modalStyle');
|
|
// 축소 상태: 인라인 스타일 제거 (CSS만 적용)
|
|
setModalStyle({});
|
|
setModalScale(1);
|
|
} else if (isOnTop && !panelInfo.modal && videoPlayer.current) {
|
|
console.log(
|
|
'[MediaPanel] Condition 3: Playing fullscreen video - enforcing 1920x1080 rendering'
|
|
);
|
|
if (videoPlayer.current?.getMediaState()?.paused) {
|
|
videoPlayer.current.play();
|
|
}
|
|
|
|
if (videoPlayer.current.areControlsVisible && !videoPlayer.current.areControlsVisible()) {
|
|
videoPlayer.current.showControls();
|
|
}
|
|
}
|
|
}, [panelInfo, isOnTop, dispatch]);
|
|
|
|
useEffect(() => {
|
|
if (!panelInfo.modal) {
|
|
console.log('[MediaPanel] modal=false - resetting inline styles for fullscreen');
|
|
setModalStyle({});
|
|
setModalScale(1);
|
|
}
|
|
}, [panelInfo.modal]);
|
|
|
|
const smallestOffsetHourIndex = useMemo(() => {
|
|
if (shopNowInfo) {
|
|
const filteredVideos = shopNowInfo.filter((video) => video.offsetHour >= currentTime);
|
|
const newSmallestOffsetHour = Math.min(...filteredVideos.map((video) => video.offsetHour));
|
|
|
|
const newSmallestOffsetHourIndex = shopNowInfo.findIndex(
|
|
(video) => video.offsetHour === newSmallestOffsetHour.toString()
|
|
);
|
|
|
|
if (shopNowInfo.length === 1) {
|
|
return 0;
|
|
}
|
|
if (newSmallestOffsetHourIndex >= 1) {
|
|
return newSmallestOffsetHourIndex - 1;
|
|
}
|
|
return newSmallestOffsetHourIndex;
|
|
}
|
|
}, [shopNowInfo, currentTime]);
|
|
|
|
const currentSubtitleUrl = useMemo(() => {
|
|
if (panelInfo?.shptmBanrTpNm === 'MEDIA') {
|
|
return panelInfo.subtitle;
|
|
}
|
|
return playListInfo && playListInfo[selectedIndex]?.showSubtitlUrl;
|
|
}, [playListInfo, selectedIndex, panelInfo]);
|
|
|
|
const currentPlayingUrl = useMemo(() => {
|
|
// return "https://download.blender.org/peach/bigbuckbunny_movies/BigBuckBunny_320x180.mp4";
|
|
|
|
if (broadcast.type === 'videoError') {
|
|
return null;
|
|
}
|
|
|
|
// MEDIA 타입일 때: panelInfo.showUrl 사용
|
|
if (panelInfo?.shptmBanrTpNm === 'MEDIA') {
|
|
// console.log('[MediaPanel]-LoadingVideo 📺 MEDIA URL:', {
|
|
// showUrl: panelInfo?.showUrl?.substring(0, 50),
|
|
// prdtId: panelInfo?.prdtId,
|
|
// });
|
|
return panelInfo?.showUrl;
|
|
}
|
|
|
|
// 기타 타입: playListInfo 사용
|
|
const url = playListInfo && playListInfo[selectedIndex]?.showUrl;
|
|
if (url) {
|
|
// console.log('[MediaPanel]-LoadingVideo 🎬 PlayList URL:', {
|
|
// url: url.substring(0, 50),
|
|
// });
|
|
}
|
|
return url;
|
|
}, [playListInfo, selectedIndex, broadcast, panelInfo?.shptmBanrTpNm, panelInfo?.showUrl]);
|
|
|
|
const isYoutube = useMemo(() => {
|
|
if (currentPlayingUrl && currentPlayingUrl.includes('youtu')) {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}, [currentPlayingUrl]);
|
|
|
|
const currentSubtitleBlob = useMemo(() => {
|
|
if (Config.DEBUG_VIDEO_SUBTITLE_TEST) {
|
|
return dummyVtt;
|
|
}
|
|
return vodSubtitleData[currentSubtitleUrl];
|
|
}, [vodSubtitleData, currentSubtitleUrl]);
|
|
|
|
const isReadyToPlay = useMemo(() => {
|
|
if (!currentPlayingUrl) {
|
|
// console.log('[MediaPanel]-LoadingVideo ❌ isReadyToPlay = false (no URL)');
|
|
return false;
|
|
}
|
|
if (!Config.DEBUG_VIDEO_SUBTITLE_TEST && currentSubtitleUrl && !currentSubtitleBlob) {
|
|
// console.log('[MediaPanel]-LoadingVideo ❌ isReadyToPlay = false (subtitle not loaded):', {
|
|
// currentSubtitleUrl,
|
|
// currentSubtitleBlob: !!currentSubtitleBlob,
|
|
// });
|
|
return false;
|
|
}
|
|
// console.log('[MediaPanel]-LoadingVideo ✅ isReadyToPlay = true');
|
|
return true;
|
|
}, [currentPlayingUrl, currentSubtitleUrl, currentSubtitleBlob, broadcast]);
|
|
|
|
const chatVisible = useMemo(() => {
|
|
if (
|
|
playListInfo &&
|
|
chatData &&
|
|
!panelInfo.modal &&
|
|
isOnTop &&
|
|
panelInfo?.shptmBanrTpNm !== 'MEDIA'
|
|
) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}, [playListInfo, chatData, panelInfo.modal, isOnTop]);
|
|
|
|
useEffect(() => {
|
|
if (currentSubtitleUrl) {
|
|
dispatch(getSubTitle({ showSubtitleUrl: currentSubtitleUrl }));
|
|
}
|
|
}, [currentSubtitleUrl]);
|
|
|
|
useEffect(() => {
|
|
setVideoLoaded(false);
|
|
}, [currentPlayingUrl]);
|
|
|
|
const handlePopupClose = useCallback(() => {
|
|
dispatch(setHidePopup());
|
|
setTimeout(() => Spotlight.focus(SpotlightIds.PLAYER_SUBTITLE_BUTTON));
|
|
}, [dispatch]);
|
|
const reactPlayerSubtitleConfig = useMemo(() => {
|
|
if (isSubtitleActive && currentSubtitleBlob) {
|
|
return {
|
|
file: {
|
|
attributes: {
|
|
crossOrigin: 'true',
|
|
},
|
|
tracks: [{ kind: 'subtitles', src: currentSubtitleBlob, default: true }],
|
|
},
|
|
youtube: YOUTUBECONFIG,
|
|
};
|
|
} else {
|
|
return {
|
|
youtube: YOUTUBECONFIG,
|
|
};
|
|
}
|
|
}, [currentSubtitleBlob, isSubtitleActive]);
|
|
|
|
const currentSideButtonStatus = useMemo(() => {
|
|
return false;
|
|
}, []);
|
|
|
|
const videoType = useMemo(() => {
|
|
if (currentPlayingUrl) {
|
|
if (currentPlayingUrl.toLowerCase().endsWith('.mp4')) {
|
|
return 'video/mp4';
|
|
} else if (currentPlayingUrl.toLowerCase().endsWith('.mpd')) {
|
|
return 'application/dash+xml';
|
|
} else if (currentPlayingUrl.toLowerCase().endsWith('.m3u8')) {
|
|
return 'application/mpegurl';
|
|
}
|
|
}
|
|
return 'application/mpegurl';
|
|
}, [currentPlayingUrl]);
|
|
|
|
const orderPhoneNumber = useMemo(() => {
|
|
if (panelInfo?.shptmBanrTpNm !== 'MEDIA' && showDetailInfo) {
|
|
return showDetailInfo[0]?.orderPhnNo;
|
|
} else {
|
|
return playListInfo[selectedIndex]?.orderPhnNo;
|
|
}
|
|
}, [panelInfo?.shptmBanrTpNm, showDetailInfo, playListInfo, selectedIndex]);
|
|
|
|
const videoThumbnailUrl = useMemo(() => {
|
|
let res = null;
|
|
|
|
if (panelInfo.shptmBanrTpNm === 'MEDIA') {
|
|
res = panelInfo?.thumbnailUrl;
|
|
} else if (playListInfo && playListInfo.length > 0) {
|
|
res = playListInfo[selectedIndex]?.thumbnailUrl;
|
|
} else if (!res) {
|
|
res = showDetailInfo[0]?.thumbnailUrl;
|
|
}
|
|
|
|
return res;
|
|
}, [
|
|
showDetailInfo,
|
|
playListInfo,
|
|
selectedIndex,
|
|
panelInfo.thumbnailUrl,
|
|
panelInfo.shptmBanrTpNm,
|
|
]);
|
|
|
|
const saveToLocalSettings = useCallback(
|
|
(showId, patnrId) => {
|
|
let recentItems = [];
|
|
if (localRecentItems) {
|
|
recentItems = [...localRecentItems];
|
|
}
|
|
|
|
const currentDate = new Date();
|
|
|
|
const formattedDate = `${currentDate.getMonth() + 1}/${currentDate.getDate()}`;
|
|
|
|
const existingProductIndex = recentItems.findIndex((item) => {
|
|
if (item.showId) return item.showId === showId;
|
|
});
|
|
|
|
if (existingProductIndex !== -1) {
|
|
recentItems.splice(existingProductIndex, 1);
|
|
}
|
|
|
|
recentItems.push({
|
|
patnrId: patnrId,
|
|
showId: showId,
|
|
date: formattedDate,
|
|
expireTime: currentDate.getTime() + 1000 * 60 * 60 * 24 * 14,
|
|
cntryCd: httpHeader['X-Device-Country'],
|
|
});
|
|
|
|
if (recentItems.length >= 51) {
|
|
const data = [...recentItems];
|
|
dispatch(changeLocalSettings({ recentItems: data.slice(1) }));
|
|
} else {
|
|
dispatch(changeLocalSettings({ recentItems }));
|
|
}
|
|
},
|
|
[httpHeader, localRecentItems, dispatch]
|
|
);
|
|
|
|
const handleIndicatorDownClick = useCallback(() => {
|
|
if (!initialEnter) {
|
|
setInitialEnter(true);
|
|
}
|
|
|
|
let newIndex = selectedIndex === playListInfo.length - 1 ? 0 : selectedIndex + 1;
|
|
let initialIndex = newIndex;
|
|
let attempts = 0;
|
|
|
|
while (!playListInfo[newIndex]?.showId && attempts < playListInfo.length) {
|
|
newIndex = newIndex === playListInfo.length - 1 ? 0 : newIndex + 1;
|
|
attempts++;
|
|
if (newIndex === initialIndex) break;
|
|
}
|
|
if (playListInfo[newIndex]?.showId) {
|
|
setSelectedIndex(newIndex);
|
|
if (panelInfo.shptmBanrTpNm === 'VOD') {
|
|
dispatch(
|
|
getMainCategoryShowDetail({
|
|
patnrId: playListInfo[newIndex]?.patnrId,
|
|
showId: playListInfo[newIndex]?.showId,
|
|
curationId: playListInfo[newIndex]?.curationId,
|
|
})
|
|
);
|
|
Spotlight.focus('playVideoShopNowBox');
|
|
} else {
|
|
dispatch(
|
|
updatePanel({
|
|
name: panel_names.MEDIA_PANEL,
|
|
panelInfo: {
|
|
chanId: playListInfo[newIndex].chanId,
|
|
patnrId: playListInfo[newIndex].patnrId,
|
|
showId: playListInfo[newIndex].showId,
|
|
shptmBanrTpNm: panelInfo?.shptmBanrTpNm,
|
|
isIndicatorByClick: true,
|
|
},
|
|
})
|
|
);
|
|
}
|
|
}
|
|
}, [dispatch, playListInfo, selectedIndex, initialEnter]);
|
|
|
|
const handleIndicatorUpClick = useCallback(() => {
|
|
if (!initialEnter) {
|
|
setInitialEnter(true);
|
|
}
|
|
|
|
let newIndex = selectedIndex === 0 ? playListInfo.length - 1 : selectedIndex - 1;
|
|
let initialIndex = newIndex;
|
|
let attempts = 0;
|
|
|
|
while (!playListInfo[newIndex]?.showId && attempts < playListInfo.length) {
|
|
newIndex = newIndex === 0 ? playListInfo.length - 1 : newIndex - 1;
|
|
attempts++;
|
|
if (newIndex === initialIndex) break;
|
|
}
|
|
|
|
if (playListInfo[newIndex]?.showId) {
|
|
setSelectedIndex(newIndex);
|
|
if (panelInfo.shptmBanrTpNm === 'VOD') {
|
|
dispatch(
|
|
getMainCategoryShowDetail({
|
|
patnrId: playListInfo[newIndex]?.patnrId,
|
|
showId: playListInfo[newIndex]?.showId,
|
|
curationId: playListInfo[newIndex]?.curationId,
|
|
})
|
|
);
|
|
Spotlight.focus('playVideoShopNowBox');
|
|
} else {
|
|
dispatch(
|
|
updatePanel({
|
|
name: panel_names.MEDIA_PANEL,
|
|
panelInfo: {
|
|
chanId: playListInfo[newIndex].chanId,
|
|
patnrId: playListInfo[newIndex].patnrId,
|
|
showId: playListInfo[newIndex].showId,
|
|
shptmBanrTpNm: panelInfo?.shptmBanrTpNm,
|
|
isIndicatorByClick: true,
|
|
},
|
|
})
|
|
);
|
|
}
|
|
}
|
|
}, [dispatch, playListInfo, selectedIndex, initialEnter]);
|
|
|
|
useEffect(() => {
|
|
if (panelInfo.shptmBanrTpNm === 'VOD' && panelInfo.patnrId && panelInfo.showId) {
|
|
//VOD의 panelInfo.showId 가 변경된 최초 한번만 호출하고, FearchedShow 항목에서 선택시 또는 상하 indicator 선택시 호출한다.
|
|
dispatch(
|
|
getMainCategoryShowDetail({
|
|
patnrId: panelInfo.patnrId,
|
|
showId: panelInfo.showId,
|
|
curationId: panelInfo.curationId,
|
|
})
|
|
);
|
|
}
|
|
}, [panelInfo.showId]);
|
|
|
|
useEffect(() => {
|
|
return () => {
|
|
dispatch(clearShopNowInfo());
|
|
dispatch(CLEAR_PLAYER_INFO());
|
|
setShopNowInfo([]);
|
|
};
|
|
}, []);
|
|
|
|
const focusBackToPanel = useCallback(() => {
|
|
if (focusReturnRef.current) {
|
|
focusReturnRef.current.focus();
|
|
return true;
|
|
}
|
|
return false;
|
|
}, []);
|
|
|
|
const focusBackButtonOrFallback = useCallback(() => {
|
|
if (Spotlight.focus('player-back-button')) {
|
|
return true;
|
|
}
|
|
return focusBackToPanel();
|
|
}, [focusBackToPanel]);
|
|
|
|
const stopExternalPlayer = useCallback(() => {
|
|
const playerInstance = videoPlayer.current;
|
|
const media = playerInstance?.video;
|
|
|
|
if (!media) {
|
|
return;
|
|
}
|
|
|
|
if (typeof media.stopVideo === 'function') {
|
|
media.stopVideo();
|
|
}
|
|
if (typeof media.seekTo === 'function') {
|
|
media.seekTo(0);
|
|
}
|
|
if (typeof media.pause === 'function') {
|
|
media.pause();
|
|
}
|
|
}, []);
|
|
|
|
const onEnded = useCallback(
|
|
(e) => {
|
|
const currentInfo = panelInfoRef.current;
|
|
|
|
// MEDIA: 기존 동작 유지 (배경 복원 없이 즉시 pop)
|
|
if (currentInfo.shptmBanrTpNm === 'MEDIA') {
|
|
console.log('[MediaPanel] 🚫 Skipping background restoration for ended media');
|
|
Spotlight.pause();
|
|
setTimeout(() => {
|
|
Spotlight.resume();
|
|
dispatch(PanelActions.popPanel());
|
|
}, VIDEO_END_ACTION_DELAY);
|
|
return;
|
|
}
|
|
|
|
// VOD: modal 여부에 따라 동작 분리
|
|
if (currentInfo.shptmBanrTpNm === 'VOD') {
|
|
Spotlight.pause();
|
|
setTimeout(() => {
|
|
stopExternalPlayer();
|
|
if (currentInfo.modal) {
|
|
// 모달: 화면 유지 + Back 포커스 회수
|
|
videoPlayer.current?.showControls?.();
|
|
Spotlight.resume();
|
|
focusBackButtonOrFallback();
|
|
} else {
|
|
// 전체화면: 바로 종료(pop)하여 포커스가 VOD 컨트롤로 남지 않게 함
|
|
Spotlight.resume();
|
|
dispatch(PanelActions.popPanel(panel_names.MEDIA_PANEL));
|
|
}
|
|
}, VIDEO_END_ACTION_DELAY);
|
|
e?.stopPropagation();
|
|
e?.preventDefault();
|
|
return;
|
|
}
|
|
},
|
|
[dispatch, focusBackButtonOrFallback, stopExternalPlayer]
|
|
);
|
|
|
|
const onKeyDown = (ev) => {
|
|
if (ev.keyCode === 34) {
|
|
handleIndicatorDownClick();
|
|
ev.stopPropagation();
|
|
ev.preventDefault();
|
|
} else if (ev.keyCode === 33) {
|
|
handleIndicatorUpClick();
|
|
ev.stopPropagation();
|
|
ev.preventDefault();
|
|
}
|
|
};
|
|
|
|
const [initialEnter, setInitialEnter] = USE_STATE('initialEnter', true);
|
|
|
|
const qrCurrentItem = useMemo(() => {
|
|
if (shopNowInfo?.length && panelInfo?.shptmBanrTpNm === 'LIVE') {
|
|
return shopNowInfo[shopNowInfo.length - 1];
|
|
}
|
|
|
|
if (
|
|
shopNowInfo?.length &&
|
|
smallestOffsetHourIndex >= 0 &&
|
|
panelInfo?.shptmBanrTpNm !== 'LIVE'
|
|
) {
|
|
return shopNowInfo[smallestOffsetHourIndex];
|
|
}
|
|
|
|
if (panelInfo?.shptmBanrTpNm === 'MEDIA' && panelInfo?.qrCurrentItem) {
|
|
return panelInfo.qrCurrentItem;
|
|
}
|
|
|
|
return null;
|
|
}, [shopNowInfo, smallestOffsetHourIndex, panelInfo?.shptmBanrTpNm, panelInfo?.qrCurrentItem]);
|
|
|
|
const isShowType = useMemo(() => {
|
|
if (['VOD', 'MEDIA'].includes(panelInfo.shptmBanrTpNm)) {
|
|
return panelInfo.shptmBanrTpNm;
|
|
}
|
|
|
|
const showType = playListInfo?.[selectedIndex]?.showType;
|
|
if (showType === 'live') return panelInfo.shptmBanrTpNm;
|
|
if (showType === 'vod') return 'VOD';
|
|
|
|
return panelInfo.shptmBanrTpNm;
|
|
}, [panelInfo.shptmBanrTpNm, playListInfo, selectedIndex]);
|
|
|
|
// Redux로 오버레이 숨김
|
|
useEffect(() => {
|
|
if (shouldHideOverlays) {
|
|
console.log('[MediaPanel] shouldHideOverlays true - 오버레이 숨김');
|
|
|
|
if (videoPlayer.current?.hideControls) {
|
|
videoPlayer.current.hideControls();
|
|
}
|
|
|
|
dispatch(resetPlayerOverlays());
|
|
}
|
|
}, [shouldHideOverlays, dispatch]);
|
|
|
|
// Redux로 오버레이 표시
|
|
useEffect(() => {
|
|
if (shouldShowOverlays) {
|
|
console.log('[MediaPanel] shouldShowOverlays true - 오버레이 표시');
|
|
|
|
if (videoPlayer.current?.showControls) {
|
|
videoPlayer.current.showControls();
|
|
}
|
|
|
|
dispatch(resetPlayerOverlays());
|
|
}
|
|
}, [shouldShowOverlays, dispatch]);
|
|
|
|
useEffect(() => {
|
|
if (panelInfoRef.current?.modal && !panelInfo.modal && isOnTop && !videoVerticalVisible) {
|
|
console.log(
|
|
'[MediaPanel] Modal to fullscreen transition detected - focus will be handled by ProductVideo'
|
|
);
|
|
// 포커스를 ProductVideo에 맡김 (ProductVideo.v3.jsx에서 처리)
|
|
// PLAYER_BACK_BUTTON으로 포커스하지 않음
|
|
}
|
|
}, [panelInfo.modal, isOnTop, videoVerticalVisible]);
|
|
|
|
useLayoutEffect(() => {
|
|
const videoContainer = document.querySelector(`.${css.videoContainer}`);
|
|
|
|
if (panelInfo.thumbnail && !videoVerticalVisible) {
|
|
videoContainer.style.background = `url(${panelInfo.thumbnail}) center center / contain no-repeat`;
|
|
videoContainer.style.backgroundColor = 'black';
|
|
}
|
|
|
|
if (broadcast.type === 'videoError' && videoThumbnailUrl) {
|
|
videoContainer.style.background = `url(${videoThumbnailUrl}) center center / contain no-repeat`;
|
|
videoContainer.style.backgroundColor = 'black';
|
|
}
|
|
}, [panelInfo.thumbnail, broadcast]);
|
|
|
|
const isPlayer = useMemo(() => {
|
|
if (!panelInfo?.modal) {
|
|
return 'full player';
|
|
}
|
|
|
|
switch (panels[0].name) {
|
|
case 'categorypanel':
|
|
return 'category';
|
|
case 'mypagepanel':
|
|
return 'my page';
|
|
case 'searchpanel':
|
|
return 'search';
|
|
case 'hotpickpanel':
|
|
return 'hot picks';
|
|
case 'featuredbrandspanel':
|
|
return 'featured brands';
|
|
case 'trendingnowpanel':
|
|
return 'trending now';
|
|
case 'playerpanel':
|
|
return 'home';
|
|
}
|
|
}, [panelInfo.modal, panels]);
|
|
|
|
const createLogParams = useCallback(
|
|
(visible) => {
|
|
if (videoLoaded && isShowType) {
|
|
if (showDetailInfo?.[0]) {
|
|
return {
|
|
visible,
|
|
showType: isShowType,
|
|
player: isPlayer,
|
|
category: showDetailInfo[0].showCatNm,
|
|
showId: showDetailInfo[0].showId,
|
|
showTitle: showDetailInfo[0].showNm,
|
|
partner: showDetailInfo[0].patncNm,
|
|
contextName: Config.LOG_CONTEXT_NAME.SHOW,
|
|
messageId: Config.LOG_MESSAGE_ID.SHOWVIEW,
|
|
};
|
|
} else if (playListInfo?.[selectedIndex]) {
|
|
const currentItem = playListInfo[selectedIndex];
|
|
return {
|
|
visible,
|
|
showType: isShowType,
|
|
player: isPlayer,
|
|
category: currentItem.catNm,
|
|
showId: currentItem.showId,
|
|
contentTitle: currentItem.showNm,
|
|
partner: currentItem.patncNm,
|
|
contextName: Config.LOG_CONTEXT_NAME.SHOW,
|
|
messageId: Config.LOG_MESSAGE_ID.SHOWVIEW,
|
|
};
|
|
}
|
|
}
|
|
return null;
|
|
},
|
|
[isShowType, videoLoaded, showDetailInfo?.[0]?.showId, playListInfo?.[selectedIndex]?.showId]
|
|
);
|
|
|
|
// isVODPaused 상태 변경 시에만 로그를 보냄
|
|
useEffect(() => {
|
|
if (showDetailInfo?.[0]) {
|
|
const params = createLogParams(!isVODPaused);
|
|
if (params) {
|
|
dispatch(sendLogTotalRecommend(params));
|
|
}
|
|
} else if (playListInfo?.[selectedIndex]) {
|
|
const params = createLogParams(true);
|
|
if (params) {
|
|
dispatch(sendLogTotalRecommend(params));
|
|
}
|
|
}
|
|
}, [isVODPaused, createLogParams, showDetailInfo]);
|
|
|
|
// 컴포넌트 언마운트 시에만 로그를 보냄
|
|
useEffect(() => {
|
|
return () => {
|
|
const params = createLogParams(false);
|
|
if (params) {
|
|
dispatch(sendLogTotalRecommend(params));
|
|
}
|
|
};
|
|
}, [createLogParams, dispatch, showDetailInfo]);
|
|
|
|
// const containerClassName = classNames(
|
|
// css.videoContainer,
|
|
// panelInfo.modal && css.modal,
|
|
// // !panelInfo.modal && css.fullscreen,
|
|
// panelInfo.shouldShrinkTo1px && css.shrinkTo1px,
|
|
// // MediaPanel이 최상단 아니고, 최상단이 DetailPanel(from Player)이면 비디오 보이도록
|
|
// !isOnTop && isTopPanelDetailFromPlayer && css['background-visible'],
|
|
// // MediaPanel이 최상단 아니고, 위 조건 아니면 1px로 숨김
|
|
// !isOnTop && !isTopPanelDetailFromPlayer && css.background,
|
|
// !captionEnable && css.hideSubtitle
|
|
// );
|
|
|
|
const containerClassName = classNames(
|
|
css.videoContainer,
|
|
panelInfo.modal && css.modal,
|
|
panelInfo.shouldShrinkTo1px && css.shrinkTo1px,
|
|
// MediaPanel이 최상단 아니고, 최상단이 DetailPanel(from Player)이면 비디오 보이도록
|
|
!isOnTop && isTopPanelDetailFromPlayer && css['background-visible'],
|
|
// MediaPanel이 최상단 아니고, 위 조건 아니면 1px로 숨김
|
|
!isOnTop && !isTopPanelDetailFromPlayer && css.background,
|
|
!captionEnable && css.hideSubtitle
|
|
);
|
|
|
|
const panelStyleOverride = !panelInfo.modal
|
|
? {
|
|
width: '100vw',
|
|
height: '100vh',
|
|
top: 0,
|
|
left: 0,
|
|
position: 'fixed',
|
|
}
|
|
: undefined;
|
|
|
|
return (
|
|
<TPanel
|
|
isTabActivated={false}
|
|
{...props}
|
|
className={containerClassName}
|
|
handleCancel={handleClickBack}
|
|
style={panelStyleOverride}
|
|
spotlightId={spotlightId}
|
|
>
|
|
<Container
|
|
spotlightRestrict={panelInfo?.modal ? 'self-only' : undefined}
|
|
spotlightId={
|
|
panelInfo?.modal ? 'spotlightId-video-contaienr' : 'fullscreen-video-container'
|
|
}
|
|
onKeyDown={onKeyDown}
|
|
>
|
|
<div
|
|
ref={focusReturnRef}
|
|
tabIndex={-1}
|
|
aria-hidden="true"
|
|
style={{ width: 1, height: 1, opacity: 0, position: 'absolute' }}
|
|
/>
|
|
{(() => {
|
|
if (isReadyToPlay) {
|
|
// console.log('[MediaPanel]-LoadingVideo 🎬 VideoPlayer 렌더링:', {
|
|
// src: currentPlayingUrl?.substring(0, 50),
|
|
// disabled: panelInfo.modal,
|
|
// cannotPlay,
|
|
// isYoutube,
|
|
// videoComponent:
|
|
// (typeof window === 'object' && !window.PalmSystem) || isYoutube
|
|
// ? 'TReactPlayer'
|
|
// : 'Media',
|
|
// });
|
|
} else {
|
|
// console.log('[MediaPanel]-LoadingVideo 🚫 VideoPlayer 렌더링 스킵됨');
|
|
}
|
|
return null;
|
|
})()}
|
|
{isReadyToPlay && (
|
|
<div className={css.videoFrame}>
|
|
<VideoPlayer
|
|
// className={!panelInfo.modal ? 'fullscreen' : undefined}
|
|
setApiProvider={getPlayer}
|
|
disabled={panelInfo.modal}
|
|
onEnded={onEnded}
|
|
noAutoPlay={false}
|
|
autoCloseTimeout={3000}
|
|
onBackButton={handleClickBack}
|
|
spotlightDisabled={panelInfo.modal}
|
|
isYoutube={isYoutube}
|
|
src={currentPlayingUrl}
|
|
style={
|
|
panelInfo.modal
|
|
? modalStyle
|
|
: {
|
|
width: '100vw',
|
|
height: '100vh',
|
|
position: 'fixed',
|
|
top: 0,
|
|
left: 0,
|
|
}
|
|
}
|
|
modalScale={panelInfo.modal ? modalScale : 1}
|
|
modalClassName={panelInfo.modal && panelInfo.modalClassName}
|
|
spotlightId={
|
|
panelInfo.modal ? 'modal-video-player' : panelInfo.modalContainerId || spotlightId
|
|
}
|
|
overlayContentsComponent={MediaOverlayContents}
|
|
handleIndicatorDownClick={handleIndicatorDownClick}
|
|
handleIndicatorUpClick={handleIndicatorUpClick}
|
|
onError={mediainfoHandler}
|
|
onTimeUpdate={mediainfoHandler}
|
|
onLoadedData={mediainfoHandler}
|
|
onLoadedMetadata={mediainfoHandler}
|
|
onDurationChange={mediainfoHandler}
|
|
reactPlayerConfig={reactPlayerSubtitleConfig}
|
|
thumbnailUrl={videoLoaded ? '' : videoThumbnailUrl}
|
|
videoComponent={
|
|
(typeof window === 'object' && !window.PalmSystem) || isYoutube
|
|
? TReactPlayer
|
|
: Media
|
|
}
|
|
liveTotalTime={liveTotalTime}
|
|
currentLiveTimeSeconds={currentLiveTimeSeconds}
|
|
countryCode={countryCode}
|
|
// VideoOverlay props
|
|
type={isShowType}
|
|
panelInfo={panelInfo}
|
|
captionEnable={captionEnable}
|
|
orderPhnNo={orderPhoneNumber}
|
|
disclaimer={shopNowInfo && shopNowInfo[smallestOffsetHourIndex]?.disclaimer}
|
|
themeProductInfos={themeProductInfos ? themeProductInfos : hotelInfos}
|
|
detailThemeProductImageLength={productImageLength}
|
|
playListInfo={playListInfo && playListInfo}
|
|
selectedIndex={selectedIndex}
|
|
qrCurrentItem={qrCurrentItem}
|
|
setIsSubtitleActive={setIsSubtitleActive}
|
|
videoVerticalVisible={videoVerticalVisible}
|
|
setCurrentTime={setCurrentTime}
|
|
setIsVODPaused={setIsVODPaused}
|
|
broadcast={broadcast}
|
|
dispatch={dispatch}
|
|
>
|
|
{typeof window === 'object' && window.PalmSystem && (
|
|
<source src={currentPlayingUrl} type={videoType} />
|
|
)}
|
|
{isSubtitleActive &&
|
|
!panelInfo.modal &&
|
|
captionEnable &&
|
|
typeof window === 'object' &&
|
|
window.PalmSystem &&
|
|
currentSubtitleBlob && (
|
|
<track kind="subtitles" src={currentSubtitleBlob} default />
|
|
)}
|
|
</VideoPlayer>
|
|
</div>
|
|
)}
|
|
|
|
{chatVisible && (
|
|
<PlayerOverlayChat
|
|
currentTime={currentTime}
|
|
videoVerticalVisible={videoVerticalVisible}
|
|
imageQRCodeUrl={playListInfo[selectedIndex]?.chatQrImgUrl}
|
|
QRCodeUrl={playListInfo[selectedIndex]?.chatUrl}
|
|
/>
|
|
)}
|
|
</Container>
|
|
|
|
{activePopup === ACTIVE_POPUP.alertPopup && (
|
|
<TPopUp
|
|
kind="textPopup"
|
|
hasText
|
|
hasButton
|
|
text={$L(
|
|
"To view the subtitles, please change the setting to 'On' in the system settings menu."
|
|
)}
|
|
button2Text={$L('OK')}
|
|
open={popupVisible}
|
|
onClose={handlePopupClose}
|
|
></TPopUp>
|
|
)}
|
|
</TPanel>
|
|
);
|
|
}
|
|
);
|
|
|
|
const propsAreEqual = (prev, next) => {
|
|
const keys = Object.keys(prev);
|
|
const nextKeys = Object.keys(next);
|
|
// if (!next.isOnTop) {
|
|
// //ignore event on background
|
|
// return true;
|
|
// }
|
|
if (keys.length !== nextKeys.length) {
|
|
return false;
|
|
}
|
|
for (let i = 0; i < keys.length; i++) {
|
|
if (prev[keys[i]] !== next[keys[i]]) {
|
|
if (JSON.stringify(prev[keys[i]]) === JSON.stringify(next[keys[i]])) {
|
|
continue;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
};
|
|
export default React.memo(MediaPanel, propsAreEqual);
|