[251019] fix: PlayerPanel Optimization-1

🕐 커밋 시간: 2025. 10. 19. 21:45:39

📊 변경 통계:
  • 총 파일: 11개
  • 추가: +119줄
  • 삭제: -101줄

📝 수정된 파일:
  ~ com.twin.app.shoptime/src/actions/productActions.js
  ~ com.twin.app.shoptime/src/components/VideoPlayer/MediaPlayer.jsx
  ~ com.twin.app.shoptime/src/components/VideoPlayer/VideoPlayer.js
  ~ com.twin.app.shoptime/src/hooks/useReviews/useReviews.js
  ~ com.twin.app.shoptime/src/reducers/productReducer.js
  ~ com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/UserReviews/UserReviews.jsx
  ~ com.twin.app.shoptime/src/views/PlayerPanel/PlayerPanel.jsx
  ~ com.twin.app.shoptime/src/views/PlayerPanel/PlayerTabContents/TabButton/PlayerTabButton.jsx
  ~ com.twin.app.shoptime/src/views/PlayerPanel/PlayerTabContents/TabContents/FeaturedShowContents.jsx
  ~ com.twin.app.shoptime/src/views/PlayerPanel/PlayerTabContents/TabContents/LiveChannelContents.jsx
  ~ com.twin.app.shoptime/src/views/PlayerPanel/PlayerTabContents/v2/TabContainer.v2.jsx

🔧 함수 변경 내용:
  📄 com.twin.app.shoptime/src/actions/productActions.js (javascript):
    🔄 Modified: resetShowAllReviews()
  📄 com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/UserReviews/UserReviews.jsx (javascript):
    🔄 Modified: SpotlightContainerDecorator()
  📄 com.twin.app.shoptime/src/views/PlayerPanel/PlayerTabContents/TabButton/PlayerTabButton.jsx (javascript):
     Deleted: handleTabOnClick()
  📄 com.twin.app.shoptime/src/views/PlayerPanel/PlayerTabContents/v2/TabContainer.v2.jsx (javascript):
    🔄 Modified: Spottable()

🔧 주요 변경 내용:
  • 핵심 비즈니스 로직 개선
  • UI 컴포넌트 아키텍처 개선
This commit is contained in:
2025-10-19 21:45:44 +09:00
parent c44866f2d1
commit 95bb25a135
11 changed files with 195 additions and 188 deletions

View File

@@ -280,15 +280,15 @@ export const getUserReviews = (requestParams) => (dispatch, getState) => {
// });
const onSuccess = (response) => {
console.log('[UserReviews]-API ✅ API 성공 응답:', {
status: response.status,
statusText: response.statusText,
headers: response.headers,
retCode: response.data && response.data.retCode,
retMsg: response.data && response.data.retMsg,
hasData: !!(response.data && response.data.data),
fullResponse: response.data,
});
// console.log('[UserReviews]-API ✅ API 성공 응답:', {
// status: response.status,
// statusText: response.statusText,
// headers: response.headers,
// retCode: response.data && response.data.retCode,
// retMsg: response.data && response.data.retMsg,
// hasData: !!(response.data && response.data.data),
// fullResponse: response.data,
// });
// ==================== [임시 테스트] 빈 리뷰 응답 시뮬레이션 - 코멘트 처리 ====================
// 30% 확률로 리뷰 없는 상품 응답 반환 (테스트용)
@@ -311,16 +311,16 @@ export const getUserReviews = (requestParams) => (dispatch, getState) => {
} */
// ==================== [임시 테스트] 끝 - 코멘트 처리 ====================
if (response.data && response.data.data) {
console.log('[UserReviews] 📊 API 데이터 상세:', {
reviewListLength: response.data.data.reviewList ? response.data.data.reviewList.length : 0,
reviewDetail: response.data.data.reviewDetail,
reviewList_sample:
(response.data.data.reviewList && response.data.data.reviewList[0]) || 'empty',
totRvwCnt: response.data.data.reviewDetail && response.data.data.reviewDetail.totRvwCnt,
totRvwAvg: response.data.data.reviewDetail && response.data.data.reviewDetail.totRvwAvg,
});
}
// if (response.data && response.data.data) {
// console.log('[UserReviews] 📊 API 데이터 상세:', {
// reviewListLength: response.data.data.reviewList ? response.data.data.reviewList.length : 0,
// reviewDetail: response.data.data.reviewDetail,
// reviewList_sample:
// (response.data.data.reviewList && response.data.data.reviewList[0]) || 'empty',
// totRvwCnt: response.data.data.reviewDetail && response.data.data.reviewDetail.totRvwCnt,
// totRvwAvg: response.data.data.reviewDetail && response.data.data.reviewDetail.totRvwAvg,
// });
// }
// 실제 API 응답에서 data 부분 추출
const apiData = extractReviewApiData(response.data);

View File

@@ -1064,9 +1064,7 @@ const VideoPlayerBase = class extends React.Component {
* @public
*/
showControls = () => {
console.log('[VideoPlayer] showControls 호출');
if (this.props.disabled) {
console.log('[VideoPlayer] disabled true - return');
return;
}
@@ -1138,10 +1136,6 @@ const VideoPlayerBase = class extends React.Component {
* @public
*/
toggleControls = () => {
console.log(
'[VideoPlayer] toggleControls 호출, mediaControlsVisible:',
this.state.mediaControlsVisible
);
if (this.state.mediaControlsVisible) {
this.hideControls();
} else {

View File

@@ -867,19 +867,11 @@ const VideoPlayerBase = class extends React.Component {
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();
}
}
@@ -1049,7 +1041,6 @@ const VideoPlayerBase = class extends React.Component {
// TabContainerV2가 표시 중이면 자동으로 닫지 않음
if (this.props.tabContainerVersion === 2 && this.props.belowContentsVisible) {
console.log('[VideoPlayer] TabContainerV2 표시 중 - autoClose 비활성화');
return;
}
@@ -1116,9 +1107,7 @@ const VideoPlayerBase = class extends React.Component {
* @public
*/
showControls = () => {
console.log('[VideoPlayer] showControls 호출');
if (this.props.disabled) {
console.log('[VideoPlayer] disabled true - return');
return;
}
@@ -1190,10 +1179,6 @@ const VideoPlayerBase = class extends React.Component {
* @public
*/
toggleControls = () => {
console.log(
'[VideoPlayer] toggleControls 호출, mediaControlsVisible:',
this.state.mediaControlsVisible
);
if (this.state.mediaControlsVisible) {
this.hideControls();
} else {
@@ -1988,12 +1973,9 @@ const VideoPlayerBase = class extends React.Component {
// Player Interaction events
//
onVideoClick = () => {
console.log('[VideoPlayer] onVideoClick 호출');
// 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);
@@ -2002,7 +1984,6 @@ const VideoPlayerBase = class extends React.Component {
// 외부 onClick 핸들러가 있으면 호출
if (this.props.onClick) {
console.log('[VideoPlayer] onClick 호출');
this.props.onClick();
return;
}

View File

@@ -239,20 +239,20 @@ const useReviews = (prdtId, patnrId) => {
});
// 별점이 없는 리뷰들 로그 출력
if (noStarReviews.length > 0) {
console.log('[UserReviews]-NoStar 별점 없는 리뷰들:', {
totalReviews: allReviews.length,
noStarCount: noStarReviews.length,
noStarReviews: noStarReviews.map((review) => ({
rvwId: review.rvwId,
rvwScr: review.rvwScr,
rvwRtng: review.rvwRtng,
rating: review.rating,
rvwCtnt: review.rvwCtnt?.substring(0, 50) + '...',
wrtrNknm: review.wrtrNknm,
})),
});
}
// if (noStarReviews.length > 0) {
// console.log('[UserReviews]-NoStar 별점 없는 리뷰들:', {
// totalReviews: allReviews.length,
// noStarCount: noStarReviews.length,
// noStarReviews: noStarReviews.map((review) => ({
// rvwId: review.rvwId,
// rvwScr: review.rvwScr,
// rvwRtng: review.rvwRtng,
// rating: review.rating,
// rvwCtnt: review.rvwCtnt?.substring(0, 50) + '...',
// wrtrNknm: review.wrtrNknm,
// })),
// });
// }
// 별점이 있는 리뷰만 카운트 (1~5점)
let ratingSum = 0;
@@ -538,14 +538,14 @@ const useReviews = (prdtId, patnrId) => {
const apiTotalCount = reviewDetail && reviewDetail.totRvwCnt ? reviewDetail.totRvwCnt : 0;
const actualReviewCount = allReviews.length;
if (apiTotalCount > actualReviewCount && actualReviewCount > 0) {
console.log('[UserReviews]-API Mismatch API 개수와 실제 개수 불일치:', {
apiTotalCount,
actualReviewCount,
difference: apiTotalCount - actualReviewCount,
reason: 'API가 최대 100개만 반환하는 페이징 제한',
});
}
// if (apiTotalCount > actualReviewCount && actualReviewCount > 0) {
// console.log('[UserReviews]-API Mismatch API 개수와 실제 개수 불일치:', {
// apiTotalCount,
// actualReviewCount,
// difference: apiTotalCount - actualReviewCount,
// reason: 'API가 최대 100개만 반환하는 페이징 제한',
// });
// }
return {
totalReviews: actualReviewCount, // 실제로 받은 리뷰 개수 사용 (API 제한 반영)

View File

@@ -1,5 +1,5 @@
import { types } from "../actions/actionTypes";
import { curry, get, set } from "../utils/fp";
import { types } from '../actions/actionTypes';
import { curry, get, set } from '../utils/fp';
const initialState = {
bestSellerData: {},
@@ -12,62 +12,57 @@ const initialState = {
// FP: handlers map (curried), pure and immutable updates only
const handleBestSeller = curry((state, action) =>
set("bestSellerData", get("payload", action), state)
set('bestSellerData', get('payload', action), state)
);
const handleProductOption = curry((state, action) =>
set("prdtOptInfo", get(["payload", "prdtOptInfo"], action), state)
set('prdtOptInfo', get(['payload', 'prdtOptInfo'], action), state)
);
const handleProductGroup = curry((state, action) =>
set("groupInfo", get(["payload", "groupInfo"], action), state)
set('groupInfo', get(['payload', 'groupInfo'], action), state)
);
const handleProductImageLength = curry((state, action) =>
set(
"productImageLength",
(get("payload", action) ? get("payload", action) : 0) + 1,
state
)
set('productImageLength', (get('payload', action) ? get('payload', action) : 0) + 1, state)
);
const handleVideoIndicatorFocus = curry((state, action) =>
set("videoIndicatorFocus", get("payload", action), state)
set('videoIndicatorFocus', get('payload', action), state)
);
const handleClearProductDetail = curry((state) => set("prdtOptInfo", null, state));
const handleClearProductDetail = curry((state) => set('prdtOptInfo', null, state));
const handleProductOptionId = curry((state, action) =>
set("prodOptCdCval", get("payload", action), state)
set('prodOptCdCval', get('payload', action), state)
);
// 유저 리뷰 데이터 핸들러 추가
const handleUserReview = curry((state, action) => {
const reviewData = get("payload", action);
const prdtId = get(["payload", "prdtId"], action);
const reviewData = get('payload', action);
const prdtId = get(['payload', 'prdtId'], action);
console.log("[UserReviews] Reducer - Storing review data:", {
prdtId: prdtId,
hasData: !!reviewData,
reviewListLength: reviewData && reviewData.reviewList ? reviewData.reviewList.length : 0,
totalCount: reviewData && reviewData.reviewDetail ? reviewData.reviewDetail.totRvwCnt : 0
});
// console.log("[UserReviews] Reducer - Storing review data:", {
// prdtId: prdtId,
// hasData: !!reviewData,
// reviewListLength: reviewData && reviewData.reviewList ? reviewData.reviewList.length : 0,
// totalCount: reviewData && reviewData.reviewDetail ? reviewData.reviewDetail.totRvwCnt : 0
// });
return set("reviewData", reviewData,
set("loadedPrdtId", prdtId, state));
return set('reviewData', reviewData, set('loadedPrdtId', prdtId, state));
});
// showAllReviews 토글 핸들러
const handleToggleShowAllReviews = curry((state, action) => {
const currentValue = get("showAllReviews", state);
const currentValue = get('showAllReviews', state);
// console.log("[UserReviews] Toggle showAllReviews:", !currentValue);
return set("showAllReviews", !currentValue, state);
return set('showAllReviews', !currentValue, state);
});
// showAllReviews 초기화 핸들러
const handleResetShowAllReviews = curry((state, action) => {
// console.log("[UserReviews] Reset showAllReviews to false");
return set("showAllReviews", false, state);
return set('showAllReviews', false, state);
});
const handlers = {
@@ -84,7 +79,7 @@ const handlers = {
};
export const productReducer = (state = initialState, action = {}) => {
const type = get("type", action);
const type = get('type', action);
const handler = handlers[type];
return handler ? handler(state, action) : state;
};

View File

@@ -169,12 +169,12 @@ export default function UserReviews({ productInfo, panelInfo, reviewsData }) {
}, []);
// 이미지 클릭으로 All-Images 모드 팝업 열기
const handleOpenAllImagesPopup = useCallback((imageIndex = 0) => {
console.log('[UserReviews] Opening popup in All-Images mode, image index:', imageIndex);
setSelectedImageIndex(imageIndex);
setPopupMode('all-images');
setIsPopupOpen(true);
}, []);
// const handleOpenAllImagesPopup = useCallback((imageIndex = 0) => {
// console.log('[UserReviews] Opening popup in All-Images mode, image index:', imageIndex);
// setSelectedImageIndex(imageIndex);
// setPopupMode('all-images');
// setIsPopupOpen(true);
// }, []);
const handleClosePopup = useCallback(() => {
// console.log("[UserReviews] Closing popup and resetting mode");

View File

@@ -269,7 +269,9 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props
const vodLogParamsRef = useRef(null);
const mediaLogParamsRef = useRef(null);
const prevNowMenuRef = useRef(null);
const watchInterval = useRef(null);
const watchIntervalLive = useRef(null);
const watchIntervalVod = useRef(null);
const watchIntervalMedia = useRef(null);
// useEffect(() => {
// console.log("###videoLoaded", videoLoaded);
// if (nowMenu) {
@@ -443,7 +445,7 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props
) {
let watchStrtDt = formatGMTString(new Date());
watchInterval.current = setInterval(() => {
watchIntervalLive.current = setInterval(() => {
let watchEndDt = formatGMTString(new Date());
let watchRecord = {
...liveLogParamsRef.current,
@@ -458,7 +460,7 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props
...status,
isModalLiveLogReady: false,
}));
clearInterval(watchInterval.current);
clearInterval(watchIntervalLive.current);
dispatch(
sendLogLive({ ...liveLogParamsRef.current, watchStrtDt }, () =>
dispatch(changeLocalSettings({ watchRecord: {} }))
@@ -476,7 +478,7 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props
) {
let watchStrtDt = formatGMTString(new Date());
watchInterval.current = setInterval(() => {
watchIntervalLive.current = setInterval(() => {
let watchEndDt = formatGMTString(new Date());
let watchRecord = {
...liveLogParamsRef.current,
@@ -491,7 +493,7 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props
...status,
isFullLiveLogReady: false,
}));
clearInterval(watchInterval.current);
clearInterval(watchIntervalLive.current);
dispatch(
sendLogLive({ ...liveLogParamsRef.current, watchStrtDt }, () =>
dispatch(changeLocalSettings({ watchRecord: {} }))
@@ -509,7 +511,7 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props
) {
let watchStrtDt = formatGMTString(new Date());
watchInterval.current = setInterval(() => {
watchIntervalLive.current = setInterval(() => {
let watchEndDt = formatGMTString(new Date());
let watchRecord = {
...liveLogParamsRef.current,
@@ -524,7 +526,7 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props
...status,
isDetailLiveLogReady: false,
}));
clearInterval(watchInterval.current);
clearInterval(watchIntervalLive.current);
dispatch(
sendLogLive({ ...liveLogParamsRef.current, watchStrtDt }, () =>
dispatch(changeLocalSettings({ watchRecord: {} }))
@@ -596,7 +598,7 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props
) {
let watchStrtDt = formatGMTString(new Date());
watchInterval.current = setInterval(() => {
watchIntervalVod.current = setInterval(() => {
let watchEndDt = formatGMTString(new Date());
let watchRecord = {
...vodLogParamsRef.current,
@@ -611,7 +613,7 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props
...status,
isModalVodLogReady: false,
}));
clearInterval(watchInterval.current);
clearInterval(watchIntervalVod.current);
dispatch(
sendLogVOD({ ...vodLogParamsRef.current, watchStrtDt }, () =>
dispatch(changeLocalSettings({ watchRecord: {} }))
@@ -629,7 +631,7 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props
) {
let watchStrtDt = formatGMTString(new Date());
watchInterval.current = setInterval(() => {
watchIntervalVod.current = setInterval(() => {
let watchEndDt = formatGMTString(new Date());
let watchRecord = {
...vodLogParamsRef.current,
@@ -644,7 +646,7 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props
...status,
isFullVodLogReady: false,
}));
clearInterval(watchInterval.current);
clearInterval(watchIntervalVod.current);
dispatch(
sendLogVOD({ ...vodLogParamsRef.current, watchStrtDt }, () =>
dispatch(changeLocalSettings({ watchRecord: {} }))
@@ -662,7 +664,7 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props
) {
let watchStrtDt = formatGMTString(new Date());
watchInterval.current = setInterval(() => {
watchIntervalVod.current = setInterval(() => {
let watchEndDt = formatGMTString(new Date());
let watchRecord = {
...vodLogParamsRef.current,
@@ -677,7 +679,7 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props
...status,
isDetailVodLogReady: false,
}));
clearInterval(watchInterval.current);
clearInterval(watchIntervalVod.current);
dispatch(
sendLogVOD({ ...vodLogParamsRef.current, watchStrtDt }, () =>
dispatch(changeLocalSettings({ watchRecord: {} }))
@@ -752,7 +754,7 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props
if (logStatus.isModalMediaLogReady && panelInfo?.modal) {
let watchStrtDt = formatGMTString(new Date());
watchInterval.current = setInterval(() => {
watchIntervalMedia.current = setInterval(() => {
let watchEndDt = formatGMTString(new Date());
let watchRecord = {
...mediaLogParamsRef.current,
@@ -767,7 +769,7 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props
...status,
isModalMediaLogReady: false,
}));
clearInterval(watchInterval.current);
clearInterval(watchIntervalMedia.current);
dispatch(
sendLogVOD({ ...mediaLogParamsRef.current, watchStrtDt }, () =>
dispatch(changeLocalSettings({ watchRecord: {} }))
@@ -781,7 +783,7 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props
if (logStatus.isFullMediaLogReady && !panelInfo?.modal) {
let watchStrtDt = formatGMTString(new Date());
watchInterval.current = setInterval(() => {
watchIntervalMedia.current = setInterval(() => {
let watchEndDt = formatGMTString(new Date());
let watchRecord = {
...mediaLogParamsRef.current,
@@ -796,7 +798,7 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props
...status,
isFullMediaLogReady: false,
}));
clearInterval(watchInterval.current);
clearInterval(watchIntervalMedia.current);
dispatch(
sendLogVOD({ ...mediaLogParamsRef.current, watchStrtDt }, () =>
dispatch(changeLocalSettings({ watchRecord: {} }))

View File

@@ -1,21 +1,21 @@
import React, { useCallback, useEffect } from "react";
import React, { useCallback, useEffect } 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 Spotlight from '@enact/spotlight';
import SpotlightContainerDecorator from '@enact/spotlight/SpotlightContainerDecorator';
import Spottable from '@enact/spotlight/Spottable';
import { updatePanel } from "../../../../actions/panelActions";
import { panel_names } from "../../../../utils/Config";
import { SpotlightIds } from "../../../../utils/SpotlightIds";
import css from "./PlayerTabButton.module.less";
import { updatePanel } from '../../../../actions/panelActions';
import { panel_names } from '../../../../utils/Config';
import { SpotlightIds } from '../../../../utils/SpotlightIds';
import css from './PlayerTabButton.module.less';
const SpottableComponent = Spottable("button");
const SpottableComponent = Spottable('button');
const Container = SpotlightContainerDecorator(
{ enterTo: "default-element", preserveld: true },
"div"
{ enterTo: 'default-element', preserveld: true },
'div'
);
export default function PlayerTabButton({
sideContentsVisible,
@@ -26,7 +26,8 @@ export default function PlayerTabButton({
const dispatch = useDispatch();
const countryCode = useSelector((state) => state.common.httpHeader.cntry_cd);
const handleTabOnClick = (e) => {
const handleTabOnClick = useCallback(
(e) => {
dispatch(
updatePanel({
name: panel_names.PLAYER_PANEL,
@@ -36,12 +37,16 @@ export default function PlayerTabButton({
})
);
setSideContentsVisible((prev) => !prev);
setTimeout(() => {
// microtask 큐를 사용하여 상태 업데이트 이후 포커스 이동
Promise.resolve().then(() => {
Spotlight.focus(SpotlightIds.PLAYER_TAB_BUTTON);
}, 0);
});
e.stopPropagation();
};
},
[dispatch, setSideContentsVisible]
);
const _onSpotlightLeft = useCallback(
(e) => {
@@ -52,7 +57,7 @@ export default function PlayerTabButton({
}
if (!sideContentsVisible) {
e.stopPropagation();
Spotlight.focus("videoIndicator-up-button");
Spotlight.focus('videoIndicator-up-button');
}
},
[sideContentsVisible]
@@ -60,10 +65,10 @@ export default function PlayerTabButton({
const onSpotlightDown = useCallback(
(e) => {
if (countryCode !== "US") {
if (videoType === "VOD") {
Spotlight.focus("videoIndicator-up-button");
} else if (videoType === "LIVE") {
if (countryCode !== 'US') {
if (videoType === 'VOD') {
Spotlight.focus('videoIndicator-up-button');
} else if (videoType === 'LIVE') {
Spotlight.focus(SpotlightIds.PLAYER_SLIDER);
}
return;
@@ -80,7 +85,7 @@ export default function PlayerTabButton({
(e) => {
if (!sideContentsVisible) {
e.stopPropagation();
Spotlight.focus("videoIndicator-up-button");
Spotlight.focus('videoIndicator-up-button');
}
},
[sideContentsVisible]

View File

@@ -1,19 +1,19 @@
import React, { useCallback, useEffect, useMemo, useRef } from "react";
import React, { useCallback, useEffect, useMemo, useRef } from 'react';
import { useDispatch } from "react-redux";
import { useDispatch } from 'react-redux';
import Spotlight from "@enact/spotlight";
import Spotlight from '@enact/spotlight';
import defaultImage from "../../../../../assets/images/img-thumb-empty-144@3x.png";
import { updatePanel } from "../../../../actions/panelActions";
import TVirtualGridList from "../../../../components/TVirtualGridList/TVirtualGridList";
import { LOG_CONTEXT_NAME, LOG_MENU, LOG_MESSAGE_ID, panel_names } from "../../../../utils/Config";
import { $L, removeSpecificTags } from "../../../../utils/helperMethods";
import PlayerItemCard, { TYPES } from "../../PlayerItemCard/PlayerItemCard";
import ListEmptyContents from "../TabContents/ListEmptyContents/ListEmptyContents";
import css from "./LiveChannelContents.module.less";
import { getMainCategoryShowDetail } from "../../../../actions/mainActions";
import { sendLogTotalRecommend } from "../../../../actions/logActions";
import defaultImage from '../../../../../assets/images/img-thumb-empty-144@3x.png';
import { updatePanel } from '../../../../actions/panelActions';
import TVirtualGridList from '../../../../components/TVirtualGridList/TVirtualGridList';
import { LOG_CONTEXT_NAME, LOG_MENU, LOG_MESSAGE_ID, panel_names } from '../../../../utils/Config';
import { $L, removeSpecificTags } from '../../../../utils/helperMethods';
import PlayerItemCard, { TYPES } from '../../PlayerItemCard/PlayerItemCard';
import ListEmptyContents from '../TabContents/ListEmptyContents/ListEmptyContents';
import css from './LiveChannelContents.module.less';
import { getMainCategoryShowDetail } from '../../../../actions/mainActions';
import { sendLogTotalRecommend } from '../../../../actions/logActions';
export default function FeaturedShowContents({
featuredShowsInfos,
@@ -25,10 +25,12 @@ export default function FeaturedShowContents({
tabIndex,
handleItemFocus,
tabTitle,
panelInfo
panelInfo,
}) {
const dispatch = useDispatch();
const isClickBlocked = useRef(false);
const blockTimeoutRef = useRef(null);
const handleFocus = useCallback(
() => () => {
if (handleItemFocus) {
@@ -61,8 +63,8 @@ export default function FeaturedShowContents({
category: catNm,
partner: patncNm,
contextName: LOG_CONTEXT_NAME.SHOW,
messageId: LOG_MESSAGE_ID.CONTENTCLICK
}
messageId: LOG_MESSAGE_ID.CONTENTCLICK,
};
dispatch(sendLogTotalRecommend(params));
//중복클릭방지
if (isClickBlocked.current) {
@@ -70,8 +72,15 @@ export default function FeaturedShowContents({
}
isClickBlocked.current = true;
setTimeout(() => {
// 이전 타이머가 있으면 정리
if (blockTimeoutRef.current) {
clearTimeout(blockTimeoutRef.current);
}
blockTimeoutRef.current = setTimeout(() => {
isClickBlocked.current = false;
blockTimeoutRef.current = null;
}, 600);
if (currentVideoShowId && currentVideoShowId === showId) {
@@ -107,29 +116,30 @@ export default function FeaturedShowContents({
spotlightId={`tabChannel-video-${index}`}
videoVerticalVisible={videoVerticalVisible}
selectedIndex={index}
currentVideoVisible={
currentVideoShowId === featuredShowsInfos[index].showId
}
currentVideoVisible={currentVideoShowId === featuredShowsInfos[index].showId}
/>
);
},
[
featuredShowsInfos,
currentVideoInfo,
currentVideoShowId,
videoVerticalVisible,
isClickBlocked,
]
[featuredShowsInfos, currentVideoInfo, currentVideoShowId, videoVerticalVisible, isClickBlocked]
);
useEffect(() => {
const timer = setTimeout(() => {
Spotlight.focus("tabChannel-video-" + selectedIndex);
Spotlight.focus('tabChannel-video-' + selectedIndex);
});
return () => clearTimeout(timer);
}, [featuredShowsInfos]);
// cleanup useEffect
useEffect(() => {
return () => {
if (blockTimeoutRef.current) {
clearTimeout(blockTimeoutRef.current);
}
};
}, []);
return (
<>
<div className={css.container}>

View File

@@ -29,6 +29,8 @@ export default function LiveChannelContents({
}) {
const dispatch = useDispatch();
const isClickBlocked = useRef(false);
const blockTimeoutRef = useRef(null);
const handleFocus = useCallback(
() => () => {
if (handleItemFocus) {
@@ -75,8 +77,15 @@ export default function LiveChannelContents({
}
isClickBlocked.current = true;
setTimeout(() => {
// 이전 타이머가 있으면 정리
if (blockTimeoutRef.current) {
clearTimeout(blockTimeoutRef.current);
}
blockTimeoutRef.current = setTimeout(() => {
isClickBlocked.current = false;
blockTimeoutRef.current = null;
}, 600);
if (!showId) return;
@@ -144,6 +153,15 @@ export default function LiveChannelContents({
const containerClass = version === 2 ? cssV2.container : css.container;
// cleanup useEffect
useEffect(() => {
return () => {
if (blockTimeoutRef.current) {
clearTimeout(blockTimeoutRef.current);
}
};
}, []);
return (
<>
<div className={containerClass}>

View File

@@ -120,9 +120,11 @@ export default function TabContainerV2({
// tabIndex가 2(ShopNowButton)로 변경되면 자동으로 포커스 이동
useEffect(() => {
if (tabIndex === 2) {
setTimeout(() => {
const timeoutId = setTimeout(() => {
Spotlight.focus('below-tab-shop-now-button');
}, 100);
return () => clearTimeout(timeoutId);
}
}, [tabIndex]);