diff --git a/com.twin.app.shoptime/src/components/VideoPlayer/VideoPlayer.js b/com.twin.app.shoptime/src/components/VideoPlayer/VideoPlayer.js
index d9d87287..40522613 100644
--- a/com.twin.app.shoptime/src/components/VideoPlayer/VideoPlayer.js
+++ b/com.twin.app.shoptime/src/components/VideoPlayer/VideoPlayer.js
@@ -710,6 +710,10 @@ const VideoPlayerBase = class extends React.Component {
reactPlayerConfig: PropTypes.any, //for ReactPlayer
qrCurrentItem: PropTypes.any,
modalScale: PropTypes.number,
+ setBelowContentsVisible: PropTypes.func,
+ belowContentsVisible: PropTypes.bool,
+ tabContainerVersion: PropTypes.number,
+ tabIndexV2: PropTypes.number,
};
static contextType = FloatingLayerContext;
@@ -854,6 +858,29 @@ const VideoPlayerBase = class extends React.Component {
this.showControls();
}
}
+
+ // TabContainerV2와 mediaControls 동기화
+ if (
+ this.props.tabContainerVersion === 2 &&
+ this.props.belowContentsVisible !== undefined &&
+ prevProps.belowContentsVisible !== this.props.belowContentsVisible
+ ) {
+ console.log('[VideoPlayer] belowContentsVisible 변경 감지:', {
+ prev: prevProps.belowContentsVisible,
+ current: this.props.belowContentsVisible,
+ mediaControlsVisible: this.state.mediaControlsVisible,
+ });
+
+ if (this.props.belowContentsVisible && !this.state.mediaControlsVisible) {
+ // TabContainerV2가 표시될 때 controls도 표시
+ console.log('[VideoPlayer] TabContainerV2 표시 - controls 강제 표시');
+ this.showControls();
+ } else if (!this.props.belowContentsVisible && this.state.mediaControlsVisible) {
+ // TabContainerV2가 숨겨질 때 controls도 숨김
+ console.log('[VideoPlayer] TabContainerV2 숨김 - controls 숨김');
+ this.hideControls();
+ }
+ }
if (
(!this.state.mediaControlsVisible &&
prevState.mediaControlsVisible !== this.state.mediaControlsVisible) ||
@@ -1016,6 +1043,13 @@ const VideoPlayerBase = class extends React.Component {
// If this.state.more is used as a reference for when this function should fire, timing for
// detection of when "more" is pressed vs when the state is updated is mismatched. Using an
// instance variable that's only set and used for this express purpose seems cleanest.
+
+ // TabContainerV2가 표시 중이면 자동으로 닫지 않음
+ if (this.props.tabContainerVersion === 2 && this.props.belowContentsVisible) {
+ console.log('[VideoPlayer] TabContainerV2 표시 중 - autoClose 비활성화');
+ return;
+ }
+
if (this.props.autoCloseTimeout && !this.props.sideContentsVisible) {
this.autoCloseJob.startAfter(this.props.autoCloseTimeout);
}
@@ -1947,10 +1981,13 @@ const VideoPlayerBase = class extends React.Component {
return;
}
- // TabContainerV2 토글
- if (this.props.setBelowContentsVisible && this.props.belowContentsVisible !== undefined) {
- console.log('[VideoPlayer] belowContentsVisible 토글:', !this.props.belowContentsVisible);
- this.props.setBelowContentsVisible(!this.props.belowContentsVisible);
+ // tabContainerVersion === 2일 때 belowContentsVisible도 함께 토글
+ if (this.props.tabContainerVersion === 2 && this.props.setBelowContentsVisible) {
+ const willShowControls = !this.state.mediaControlsVisible;
+ console.log('[VideoPlayer] 클릭 - 상태 동기화 토글:', willShowControls);
+
+ // belowContentsVisible을 먼저 변경 (componentDidUpdate에서 mediaControls 동기화됨)
+ this.props.setBelowContentsVisible(willShowControls);
}
this.toggleControls();
@@ -2152,6 +2189,8 @@ const VideoPlayerBase = class extends React.Component {
isYoutube,
sideContentsVisible,
setSideContentsVisible,
+ belowContentsVisible,
+ tabContainerVersion,
disclaimer,
liveTotalTime,
currentLiveTimeSeconds,
@@ -2196,6 +2235,10 @@ const VideoPlayerBase = class extends React.Component {
delete mediaProps.thumbnailUnavailable;
delete mediaProps.titleHideDelay;
delete mediaProps.videoPath;
+ delete mediaProps.setBelowContentsVisible;
+ delete mediaProps.belowContentsVisible;
+ delete mediaProps.tabContainerVersion;
+ delete mediaProps.tabIndexV2;
mediaProps.autoPlay = !noAutoPlay;
mediaProps.className = type !== 'MEDIA' ? css.video : css.media;
@@ -2393,6 +2436,9 @@ const VideoPlayerBase = class extends React.Component {
setIsSubtitleActive={setIsSubtitleActive}
sideContentsVisible={sideContentsVisible}
setSideContentsVisible={setSideContentsVisible}
+ belowContentsVisible={belowContentsVisible}
+ tabContainerVersion={tabContainerVersion}
+ tabIndexV2={this.props.tabIndexV2}
videoVerticalVisible={videoVerticalVisible}
handleIndicatorUpClick={handleIndicatorUpClick}
handleIndicatorDownClick={handleIndicatorDownClick}
diff --git a/com.twin.app.shoptime/src/views/PlayerPanel/PlayerOverlay/PlayerOverlayContents.jsx b/com.twin.app.shoptime/src/views/PlayerPanel/PlayerOverlay/PlayerOverlayContents.jsx
index e81f155f..8dc7c684 100644
--- a/com.twin.app.shoptime/src/views/PlayerPanel/PlayerOverlay/PlayerOverlayContents.jsx
+++ b/com.twin.app.shoptime/src/views/PlayerPanel/PlayerOverlay/PlayerOverlayContents.jsx
@@ -1,27 +1,24 @@
-import React, { useCallback, useEffect, useMemo, useRef } from "react";
+import React, { useCallback, useEffect, useMemo, useRef } from 'react';
-import classNames from "classnames";
-import { useDispatch, useSelector } from "react-redux";
+import classNames from 'classnames';
+import { useDispatch, useSelector } from 'react-redux';
-import Spotlight from "@enact/spotlight";
-import SpotlightContainerDecorator from "@enact/spotlight/SpotlightContainerDecorator";
-import Spottable from "@enact/spotlight/Spottable";
-import Marquee from "@enact/ui/Marquee";
+import Spotlight from '@enact/spotlight';
+import SpotlightContainerDecorator from '@enact/spotlight/SpotlightContainerDecorator';
+import Spottable from '@enact/spotlight/Spottable';
+import Marquee from '@enact/ui/Marquee';
-import defaultLogoImg from "../../../../assets/images/ic-tab-partners-default@3x.png";
-import { setShowPopup } from "../../../actions/commonActions";
-import CustomImage from "../../../components/CustomImage/CustomImage";
-import { ACTIVE_POPUP } from "../../../utils/Config";
-import { SpotlightIds } from "../../../utils/SpotlightIds";
-import PlayerTabButton from "../PlayerTabContents/TabButton/PlayerTabButton";
-import css from "./PlayerOverlayContents.module.less";
+import defaultLogoImg from '../../../../assets/images/ic-tab-partners-default@3x.png';
+import { setShowPopup } from '../../../actions/commonActions';
+import CustomImage from '../../../components/CustomImage/CustomImage';
+import { ACTIVE_POPUP } from '../../../utils/Config';
+import { SpotlightIds } from '../../../utils/SpotlightIds';
+import PlayerTabButton from '../PlayerTabContents/TabButton/PlayerTabButton';
+import css from './PlayerOverlayContents.module.less';
-const SpottableBtn = Spottable("button");
+const SpottableBtn = Spottable('button');
-const Container = SpotlightContainerDecorator(
- { enterTo: "default-element" },
- "div"
-);
+const Container = SpotlightContainerDecorator({ enterTo: 'default-element' }, 'div');
export default function PlayerOverlayContents({
type,
@@ -35,8 +32,11 @@ export default function PlayerOverlayContents({
videoVerticalVisible,
sideContentsVisible,
setSideContentsVisible,
+ belowContentsVisible,
handleIndicatorUpClick,
handleIndicatorDownClick,
+ tabContainerVersion,
+ tabIndexV2,
...rest
}) {
const cntry_cd = useSelector((state) => state.common.httpHeader?.cntry_cd);
@@ -49,7 +49,7 @@ export default function PlayerOverlayContents({
const backBtnRef = useRef(null);
useEffect(() => {
- if (type === "MEDIA" && !panelInfo.modal && backBtnRef.current) {
+ if (type === 'MEDIA' && !panelInfo.modal && backBtnRef.current) {
Spotlight.focus(SpotlightIds.PLAYER_BACK_BUTTON);
}
}, [panelInfo?.shptmBanrTpNm, panelInfo.modal, backBtnRef.current]);
@@ -64,7 +64,7 @@ export default function PlayerOverlayContents({
const patncLogoPath = useMemo(() => {
let logo = playListInfo[selectedIndex]?.patncLogoPath;
- if (type === "MEDIA") {
+ if (type === 'MEDIA') {
logo = panelInfo?.patncLogoPath;
}
@@ -73,7 +73,7 @@ export default function PlayerOverlayContents({
const partnerName = useMemo(() => {
let name = playListInfo[selectedIndex]?.patncNm;
- if (type === "MEDIA") {
+ if (type === 'MEDIA') {
name = panelInfo?.patncNm;
}
@@ -82,11 +82,11 @@ export default function PlayerOverlayContents({
const showName = useMemo(() => {
let name = playListInfo[selectedIndex]?.showNm;
- if (type === "MEDIA") {
+ if (type === 'MEDIA') {
name = panelInfo?.showNm;
}
- return name ? name.replace(/
/gi, " ") : "";
+ return name ? name.replace(/
/gi, ' ') : '';
}, [playListInfo, selectedIndex, panelInfo]);
const onSpotlightMoveTabButton = (e) => {
@@ -97,15 +97,15 @@ export default function PlayerOverlayContents({
const onSpotlightMoveMediaButton = (e) => {
e.stopPropagation();
- if (type === "LIVE") {
- return Spotlight.focus("videoIndicator-down-button");
+ if (type === 'LIVE') {
+ return Spotlight.focus('videoIndicator-down-button');
}
- Spotlight.focus("videoPlayer_mediaControls");
+ Spotlight.focus('videoPlayer_mediaControls');
};
const onSpotlightMoveSlider = useCallback(
(e) => {
- if (type === "VOD") {
+ if (type === 'VOD') {
e.stopPropagation();
Spotlight.focus(SpotlightIds.PLAYER_SLIDER);
@@ -117,7 +117,24 @@ export default function PlayerOverlayContents({
const onSpotlightMoveSideTab = (e) => {
e.stopPropagation();
e.preventDefault();
- Spotlight.focus("tab-0");
+ Spotlight.focus('tab-0');
+ };
+
+ const onSpotlightMoveBelowTab = (e) => {
+ e.stopPropagation();
+ e.preventDefault();
+
+ // tabIndexV2에 따라 다른 버튼으로 포커스 이동
+ if (tabIndexV2 === 0) {
+ // ShopNow 탭: Close 버튼으로
+ Spotlight.focus('below-tab-close-button');
+ } else if (tabIndexV2 === 1) {
+ // LIVE CHANNEL 탭: LIVE CHANNEL 버튼으로
+ Spotlight.focus('below-tab-live-channel-button');
+ } else if (tabIndexV2 === 2) {
+ // ShopNowButton: ShopNowButton으로
+ Spotlight.focus('below-tab-shop-now-button');
+ }
};
const onSpotlightMoveBackButton = () => {
@@ -125,11 +142,16 @@ export default function PlayerOverlayContents({
};
const currentSideButtonStatus = useMemo(() => {
- if (type !== "MEDIA" && !panelInfo?.modal && !sideContentsVisible) {
+ if (
+ type !== 'MEDIA' &&
+ !panelInfo?.modal &&
+ !sideContentsVisible &&
+ tabContainerVersion === 1
+ ) {
return true;
}
return false;
- }, [panelInfo, sideContentsVisible]);
+ }, [panelInfo, sideContentsVisible, tabContainerVersion]);
const noLiveContentsVisible = useMemo(() => {
if (!Array.isArray(playListInfo) || playListInfo.length === 0) {
@@ -147,54 +169,63 @@ export default function PlayerOverlayContents({
return (
<>
{partnerName}