[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:
@@ -280,15 +280,15 @@ export const getUserReviews = (requestParams) => (dispatch, getState) => {
|
|||||||
// });
|
// });
|
||||||
|
|
||||||
const onSuccess = (response) => {
|
const onSuccess = (response) => {
|
||||||
console.log('[UserReviews]-API ✅ API 성공 응답:', {
|
// console.log('[UserReviews]-API ✅ API 성공 응답:', {
|
||||||
status: response.status,
|
// status: response.status,
|
||||||
statusText: response.statusText,
|
// statusText: response.statusText,
|
||||||
headers: response.headers,
|
// headers: response.headers,
|
||||||
retCode: response.data && response.data.retCode,
|
// retCode: response.data && response.data.retCode,
|
||||||
retMsg: response.data && response.data.retMsg,
|
// retMsg: response.data && response.data.retMsg,
|
||||||
hasData: !!(response.data && response.data.data),
|
// hasData: !!(response.data && response.data.data),
|
||||||
fullResponse: response.data,
|
// fullResponse: response.data,
|
||||||
});
|
// });
|
||||||
|
|
||||||
// ==================== [임시 테스트] 빈 리뷰 응답 시뮬레이션 - 코멘트 처리 ====================
|
// ==================== [임시 테스트] 빈 리뷰 응답 시뮬레이션 - 코멘트 처리 ====================
|
||||||
// 30% 확률로 리뷰 없는 상품 응답 반환 (테스트용)
|
// 30% 확률로 리뷰 없는 상품 응답 반환 (테스트용)
|
||||||
@@ -311,16 +311,16 @@ export const getUserReviews = (requestParams) => (dispatch, getState) => {
|
|||||||
} */
|
} */
|
||||||
// ==================== [임시 테스트] 끝 - 코멘트 처리 ====================
|
// ==================== [임시 테스트] 끝 - 코멘트 처리 ====================
|
||||||
|
|
||||||
if (response.data && response.data.data) {
|
// if (response.data && response.data.data) {
|
||||||
console.log('[UserReviews] 📊 API 데이터 상세:', {
|
// console.log('[UserReviews] 📊 API 데이터 상세:', {
|
||||||
reviewListLength: response.data.data.reviewList ? response.data.data.reviewList.length : 0,
|
// reviewListLength: response.data.data.reviewList ? response.data.data.reviewList.length : 0,
|
||||||
reviewDetail: response.data.data.reviewDetail,
|
// reviewDetail: response.data.data.reviewDetail,
|
||||||
reviewList_sample:
|
// reviewList_sample:
|
||||||
(response.data.data.reviewList && response.data.data.reviewList[0]) || 'empty',
|
// (response.data.data.reviewList && response.data.data.reviewList[0]) || 'empty',
|
||||||
totRvwCnt: response.data.data.reviewDetail && response.data.data.reviewDetail.totRvwCnt,
|
// totRvwCnt: response.data.data.reviewDetail && response.data.data.reviewDetail.totRvwCnt,
|
||||||
totRvwAvg: response.data.data.reviewDetail && response.data.data.reviewDetail.totRvwAvg,
|
// totRvwAvg: response.data.data.reviewDetail && response.data.data.reviewDetail.totRvwAvg,
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
|
|
||||||
// 실제 API 응답에서 data 부분 추출
|
// 실제 API 응답에서 data 부분 추출
|
||||||
const apiData = extractReviewApiData(response.data);
|
const apiData = extractReviewApiData(response.data);
|
||||||
|
|||||||
@@ -1064,9 +1064,7 @@ const VideoPlayerBase = class extends React.Component {
|
|||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
showControls = () => {
|
showControls = () => {
|
||||||
console.log('[VideoPlayer] showControls 호출');
|
|
||||||
if (this.props.disabled) {
|
if (this.props.disabled) {
|
||||||
console.log('[VideoPlayer] disabled true - return');
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1138,10 +1136,6 @@ const VideoPlayerBase = class extends React.Component {
|
|||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
toggleControls = () => {
|
toggleControls = () => {
|
||||||
console.log(
|
|
||||||
'[VideoPlayer] toggleControls 호출, mediaControlsVisible:',
|
|
||||||
this.state.mediaControlsVisible
|
|
||||||
);
|
|
||||||
if (this.state.mediaControlsVisible) {
|
if (this.state.mediaControlsVisible) {
|
||||||
this.hideControls();
|
this.hideControls();
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -867,19 +867,11 @@ const VideoPlayerBase = class extends React.Component {
|
|||||||
this.props.belowContentsVisible !== undefined &&
|
this.props.belowContentsVisible !== undefined &&
|
||||||
prevProps.belowContentsVisible !== this.props.belowContentsVisible
|
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) {
|
if (this.props.belowContentsVisible && !this.state.mediaControlsVisible) {
|
||||||
// TabContainerV2가 표시될 때 controls도 표시
|
// TabContainerV2가 표시될 때 controls도 표시
|
||||||
console.log('[VideoPlayer] TabContainerV2 표시 - controls 강제 표시');
|
|
||||||
this.showControls();
|
this.showControls();
|
||||||
} else if (!this.props.belowContentsVisible && this.state.mediaControlsVisible) {
|
} else if (!this.props.belowContentsVisible && this.state.mediaControlsVisible) {
|
||||||
// TabContainerV2가 숨겨질 때 controls도 숨김
|
// TabContainerV2가 숨겨질 때 controls도 숨김
|
||||||
console.log('[VideoPlayer] TabContainerV2 숨김 - controls 숨김');
|
|
||||||
this.hideControls();
|
this.hideControls();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1049,7 +1041,6 @@ const VideoPlayerBase = class extends React.Component {
|
|||||||
|
|
||||||
// TabContainerV2가 표시 중이면 자동으로 닫지 않음
|
// TabContainerV2가 표시 중이면 자동으로 닫지 않음
|
||||||
if (this.props.tabContainerVersion === 2 && this.props.belowContentsVisible) {
|
if (this.props.tabContainerVersion === 2 && this.props.belowContentsVisible) {
|
||||||
console.log('[VideoPlayer] TabContainerV2 표시 중 - autoClose 비활성화');
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1116,9 +1107,7 @@ const VideoPlayerBase = class extends React.Component {
|
|||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
showControls = () => {
|
showControls = () => {
|
||||||
console.log('[VideoPlayer] showControls 호출');
|
|
||||||
if (this.props.disabled) {
|
if (this.props.disabled) {
|
||||||
console.log('[VideoPlayer] disabled true - return');
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1190,10 +1179,6 @@ const VideoPlayerBase = class extends React.Component {
|
|||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
toggleControls = () => {
|
toggleControls = () => {
|
||||||
console.log(
|
|
||||||
'[VideoPlayer] toggleControls 호출, mediaControlsVisible:',
|
|
||||||
this.state.mediaControlsVisible
|
|
||||||
);
|
|
||||||
if (this.state.mediaControlsVisible) {
|
if (this.state.mediaControlsVisible) {
|
||||||
this.hideControls();
|
this.hideControls();
|
||||||
} else {
|
} else {
|
||||||
@@ -1988,12 +1973,9 @@ const VideoPlayerBase = class extends React.Component {
|
|||||||
// Player Interaction events
|
// Player Interaction events
|
||||||
//
|
//
|
||||||
onVideoClick = () => {
|
onVideoClick = () => {
|
||||||
console.log('[VideoPlayer] onVideoClick 호출');
|
|
||||||
|
|
||||||
// tabContainerVersion === 2일 때 belowContentsVisible도 함께 토글
|
// tabContainerVersion === 2일 때 belowContentsVisible도 함께 토글
|
||||||
if (this.props.tabContainerVersion === 2 && this.props.setBelowContentsVisible) {
|
if (this.props.tabContainerVersion === 2 && this.props.setBelowContentsVisible) {
|
||||||
const willShowControls = !this.state.mediaControlsVisible;
|
const willShowControls = !this.state.mediaControlsVisible;
|
||||||
console.log('[VideoPlayer] 클릭 - 상태 동기화 토글:', willShowControls);
|
|
||||||
|
|
||||||
// belowContentsVisible을 먼저 변경 (componentDidUpdate에서 mediaControls 동기화됨)
|
// belowContentsVisible을 먼저 변경 (componentDidUpdate에서 mediaControls 동기화됨)
|
||||||
this.props.setBelowContentsVisible(willShowControls);
|
this.props.setBelowContentsVisible(willShowControls);
|
||||||
@@ -2002,7 +1984,6 @@ const VideoPlayerBase = class extends React.Component {
|
|||||||
|
|
||||||
// 외부 onClick 핸들러가 있으면 호출
|
// 외부 onClick 핸들러가 있으면 호출
|
||||||
if (this.props.onClick) {
|
if (this.props.onClick) {
|
||||||
console.log('[VideoPlayer] onClick 호출');
|
|
||||||
this.props.onClick();
|
this.props.onClick();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -239,20 +239,20 @@ const useReviews = (prdtId, patnrId) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// 별점이 없는 리뷰들 로그 출력
|
// 별점이 없는 리뷰들 로그 출력
|
||||||
if (noStarReviews.length > 0) {
|
// if (noStarReviews.length > 0) {
|
||||||
console.log('[UserReviews]-NoStar 별점 없는 리뷰들:', {
|
// console.log('[UserReviews]-NoStar 별점 없는 리뷰들:', {
|
||||||
totalReviews: allReviews.length,
|
// totalReviews: allReviews.length,
|
||||||
noStarCount: noStarReviews.length,
|
// noStarCount: noStarReviews.length,
|
||||||
noStarReviews: noStarReviews.map((review) => ({
|
// noStarReviews: noStarReviews.map((review) => ({
|
||||||
rvwId: review.rvwId,
|
// rvwId: review.rvwId,
|
||||||
rvwScr: review.rvwScr,
|
// rvwScr: review.rvwScr,
|
||||||
rvwRtng: review.rvwRtng,
|
// rvwRtng: review.rvwRtng,
|
||||||
rating: review.rating,
|
// rating: review.rating,
|
||||||
rvwCtnt: review.rvwCtnt?.substring(0, 50) + '...',
|
// rvwCtnt: review.rvwCtnt?.substring(0, 50) + '...',
|
||||||
wrtrNknm: review.wrtrNknm,
|
// wrtrNknm: review.wrtrNknm,
|
||||||
})),
|
// })),
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
|
|
||||||
// 별점이 있는 리뷰만 카운트 (1~5점)
|
// 별점이 있는 리뷰만 카운트 (1~5점)
|
||||||
let ratingSum = 0;
|
let ratingSum = 0;
|
||||||
@@ -538,14 +538,14 @@ const useReviews = (prdtId, patnrId) => {
|
|||||||
const apiTotalCount = reviewDetail && reviewDetail.totRvwCnt ? reviewDetail.totRvwCnt : 0;
|
const apiTotalCount = reviewDetail && reviewDetail.totRvwCnt ? reviewDetail.totRvwCnt : 0;
|
||||||
const actualReviewCount = allReviews.length;
|
const actualReviewCount = allReviews.length;
|
||||||
|
|
||||||
if (apiTotalCount > actualReviewCount && actualReviewCount > 0) {
|
// if (apiTotalCount > actualReviewCount && actualReviewCount > 0) {
|
||||||
console.log('[UserReviews]-API Mismatch API 개수와 실제 개수 불일치:', {
|
// console.log('[UserReviews]-API Mismatch API 개수와 실제 개수 불일치:', {
|
||||||
apiTotalCount,
|
// apiTotalCount,
|
||||||
actualReviewCount,
|
// actualReviewCount,
|
||||||
difference: apiTotalCount - actualReviewCount,
|
// difference: apiTotalCount - actualReviewCount,
|
||||||
reason: 'API가 최대 100개만 반환하는 페이징 제한',
|
// reason: 'API가 최대 100개만 반환하는 페이징 제한',
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
|
|
||||||
return {
|
return {
|
||||||
totalReviews: actualReviewCount, // 실제로 받은 리뷰 개수 사용 (API 제한 반영)
|
totalReviews: actualReviewCount, // 실제로 받은 리뷰 개수 사용 (API 제한 반영)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { types } from "../actions/actionTypes";
|
import { types } from '../actions/actionTypes';
|
||||||
import { curry, get, set } from "../utils/fp";
|
import { curry, get, set } from '../utils/fp';
|
||||||
|
|
||||||
const initialState = {
|
const initialState = {
|
||||||
bestSellerData: {},
|
bestSellerData: {},
|
||||||
@@ -12,62 +12,57 @@ const initialState = {
|
|||||||
|
|
||||||
// FP: handlers map (curried), pure and immutable updates only
|
// FP: handlers map (curried), pure and immutable updates only
|
||||||
const handleBestSeller = curry((state, action) =>
|
const handleBestSeller = curry((state, action) =>
|
||||||
set("bestSellerData", get("payload", action), state)
|
set('bestSellerData', get('payload', action), state)
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleProductOption = curry((state, action) =>
|
const handleProductOption = curry((state, action) =>
|
||||||
set("prdtOptInfo", get(["payload", "prdtOptInfo"], action), state)
|
set('prdtOptInfo', get(['payload', 'prdtOptInfo'], action), state)
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleProductGroup = curry((state, action) =>
|
const handleProductGroup = curry((state, action) =>
|
||||||
set("groupInfo", get(["payload", "groupInfo"], action), state)
|
set('groupInfo', get(['payload', 'groupInfo'], action), state)
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleProductImageLength = curry((state, action) =>
|
const handleProductImageLength = curry((state, action) =>
|
||||||
set(
|
set('productImageLength', (get('payload', action) ? get('payload', action) : 0) + 1, state)
|
||||||
"productImageLength",
|
|
||||||
(get("payload", action) ? get("payload", action) : 0) + 1,
|
|
||||||
state
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleVideoIndicatorFocus = curry((state, action) =>
|
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) =>
|
const handleProductOptionId = curry((state, action) =>
|
||||||
set("prodOptCdCval", get("payload", action), state)
|
set('prodOptCdCval', get('payload', action), state)
|
||||||
);
|
);
|
||||||
|
|
||||||
// 유저 리뷰 데이터 핸들러 추가
|
// 유저 리뷰 데이터 핸들러 추가
|
||||||
const handleUserReview = curry((state, action) => {
|
const handleUserReview = curry((state, action) => {
|
||||||
const reviewData = get("payload", action);
|
const reviewData = get('payload', action);
|
||||||
const prdtId = get(["payload", "prdtId"], action);
|
const prdtId = get(['payload', 'prdtId'], action);
|
||||||
|
|
||||||
console.log("[UserReviews] Reducer - Storing review data:", {
|
// console.log("[UserReviews] Reducer - Storing review data:", {
|
||||||
prdtId: prdtId,
|
// prdtId: prdtId,
|
||||||
hasData: !!reviewData,
|
// hasData: !!reviewData,
|
||||||
reviewListLength: reviewData && reviewData.reviewList ? reviewData.reviewList.length : 0,
|
// reviewListLength: reviewData && reviewData.reviewList ? reviewData.reviewList.length : 0,
|
||||||
totalCount: reviewData && reviewData.reviewDetail ? reviewData.reviewDetail.totRvwCnt : 0
|
// totalCount: reviewData && reviewData.reviewDetail ? reviewData.reviewDetail.totRvwCnt : 0
|
||||||
});
|
// });
|
||||||
|
|
||||||
return set("reviewData", reviewData,
|
return set('reviewData', reviewData, set('loadedPrdtId', prdtId, state));
|
||||||
set("loadedPrdtId", prdtId, state));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// showAllReviews 토글 핸들러
|
// showAllReviews 토글 핸들러
|
||||||
const handleToggleShowAllReviews = curry((state, action) => {
|
const handleToggleShowAllReviews = curry((state, action) => {
|
||||||
const currentValue = get("showAllReviews", state);
|
const currentValue = get('showAllReviews', state);
|
||||||
// console.log("[UserReviews] Toggle showAllReviews:", !currentValue);
|
// console.log("[UserReviews] Toggle showAllReviews:", !currentValue);
|
||||||
return set("showAllReviews", !currentValue, state);
|
return set('showAllReviews', !currentValue, state);
|
||||||
});
|
});
|
||||||
|
|
||||||
// showAllReviews 초기화 핸들러
|
// showAllReviews 초기화 핸들러
|
||||||
const handleResetShowAllReviews = curry((state, action) => {
|
const handleResetShowAllReviews = curry((state, action) => {
|
||||||
// console.log("[UserReviews] Reset showAllReviews to false");
|
// console.log("[UserReviews] Reset showAllReviews to false");
|
||||||
return set("showAllReviews", false, state);
|
return set('showAllReviews', false, state);
|
||||||
});
|
});
|
||||||
|
|
||||||
const handlers = {
|
const handlers = {
|
||||||
@@ -84,7 +79,7 @@ const handlers = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const productReducer = (state = initialState, action = {}) => {
|
export const productReducer = (state = initialState, action = {}) => {
|
||||||
const type = get("type", action);
|
const type = get('type', action);
|
||||||
const handler = handlers[type];
|
const handler = handlers[type];
|
||||||
return handler ? handler(state, action) : state;
|
return handler ? handler(state, action) : state;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -169,12 +169,12 @@ export default function UserReviews({ productInfo, panelInfo, reviewsData }) {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// 이미지 클릭으로 All-Images 모드 팝업 열기
|
// 이미지 클릭으로 All-Images 모드 팝업 열기
|
||||||
const handleOpenAllImagesPopup = useCallback((imageIndex = 0) => {
|
// const handleOpenAllImagesPopup = useCallback((imageIndex = 0) => {
|
||||||
console.log('[UserReviews] Opening popup in All-Images mode, image index:', imageIndex);
|
// console.log('[UserReviews] Opening popup in All-Images mode, image index:', imageIndex);
|
||||||
setSelectedImageIndex(imageIndex);
|
// setSelectedImageIndex(imageIndex);
|
||||||
setPopupMode('all-images');
|
// setPopupMode('all-images');
|
||||||
setIsPopupOpen(true);
|
// setIsPopupOpen(true);
|
||||||
}, []);
|
// }, []);
|
||||||
|
|
||||||
const handleClosePopup = useCallback(() => {
|
const handleClosePopup = useCallback(() => {
|
||||||
// console.log("[UserReviews] Closing popup and resetting mode");
|
// console.log("[UserReviews] Closing popup and resetting mode");
|
||||||
|
|||||||
@@ -269,7 +269,9 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props
|
|||||||
const vodLogParamsRef = useRef(null);
|
const vodLogParamsRef = useRef(null);
|
||||||
const mediaLogParamsRef = useRef(null);
|
const mediaLogParamsRef = useRef(null);
|
||||||
const prevNowMenuRef = useRef(null);
|
const prevNowMenuRef = useRef(null);
|
||||||
const watchInterval = useRef(null);
|
const watchIntervalLive = useRef(null);
|
||||||
|
const watchIntervalVod = useRef(null);
|
||||||
|
const watchIntervalMedia = useRef(null);
|
||||||
// useEffect(() => {
|
// useEffect(() => {
|
||||||
// console.log("###videoLoaded", videoLoaded);
|
// console.log("###videoLoaded", videoLoaded);
|
||||||
// if (nowMenu) {
|
// if (nowMenu) {
|
||||||
@@ -443,7 +445,7 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props
|
|||||||
) {
|
) {
|
||||||
let watchStrtDt = formatGMTString(new Date());
|
let watchStrtDt = formatGMTString(new Date());
|
||||||
|
|
||||||
watchInterval.current = setInterval(() => {
|
watchIntervalLive.current = setInterval(() => {
|
||||||
let watchEndDt = formatGMTString(new Date());
|
let watchEndDt = formatGMTString(new Date());
|
||||||
let watchRecord = {
|
let watchRecord = {
|
||||||
...liveLogParamsRef.current,
|
...liveLogParamsRef.current,
|
||||||
@@ -458,7 +460,7 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props
|
|||||||
...status,
|
...status,
|
||||||
isModalLiveLogReady: false,
|
isModalLiveLogReady: false,
|
||||||
}));
|
}));
|
||||||
clearInterval(watchInterval.current);
|
clearInterval(watchIntervalLive.current);
|
||||||
dispatch(
|
dispatch(
|
||||||
sendLogLive({ ...liveLogParamsRef.current, watchStrtDt }, () =>
|
sendLogLive({ ...liveLogParamsRef.current, watchStrtDt }, () =>
|
||||||
dispatch(changeLocalSettings({ watchRecord: {} }))
|
dispatch(changeLocalSettings({ watchRecord: {} }))
|
||||||
@@ -476,7 +478,7 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props
|
|||||||
) {
|
) {
|
||||||
let watchStrtDt = formatGMTString(new Date());
|
let watchStrtDt = formatGMTString(new Date());
|
||||||
|
|
||||||
watchInterval.current = setInterval(() => {
|
watchIntervalLive.current = setInterval(() => {
|
||||||
let watchEndDt = formatGMTString(new Date());
|
let watchEndDt = formatGMTString(new Date());
|
||||||
let watchRecord = {
|
let watchRecord = {
|
||||||
...liveLogParamsRef.current,
|
...liveLogParamsRef.current,
|
||||||
@@ -491,7 +493,7 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props
|
|||||||
...status,
|
...status,
|
||||||
isFullLiveLogReady: false,
|
isFullLiveLogReady: false,
|
||||||
}));
|
}));
|
||||||
clearInterval(watchInterval.current);
|
clearInterval(watchIntervalLive.current);
|
||||||
dispatch(
|
dispatch(
|
||||||
sendLogLive({ ...liveLogParamsRef.current, watchStrtDt }, () =>
|
sendLogLive({ ...liveLogParamsRef.current, watchStrtDt }, () =>
|
||||||
dispatch(changeLocalSettings({ watchRecord: {} }))
|
dispatch(changeLocalSettings({ watchRecord: {} }))
|
||||||
@@ -509,7 +511,7 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props
|
|||||||
) {
|
) {
|
||||||
let watchStrtDt = formatGMTString(new Date());
|
let watchStrtDt = formatGMTString(new Date());
|
||||||
|
|
||||||
watchInterval.current = setInterval(() => {
|
watchIntervalLive.current = setInterval(() => {
|
||||||
let watchEndDt = formatGMTString(new Date());
|
let watchEndDt = formatGMTString(new Date());
|
||||||
let watchRecord = {
|
let watchRecord = {
|
||||||
...liveLogParamsRef.current,
|
...liveLogParamsRef.current,
|
||||||
@@ -524,7 +526,7 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props
|
|||||||
...status,
|
...status,
|
||||||
isDetailLiveLogReady: false,
|
isDetailLiveLogReady: false,
|
||||||
}));
|
}));
|
||||||
clearInterval(watchInterval.current);
|
clearInterval(watchIntervalLive.current);
|
||||||
dispatch(
|
dispatch(
|
||||||
sendLogLive({ ...liveLogParamsRef.current, watchStrtDt }, () =>
|
sendLogLive({ ...liveLogParamsRef.current, watchStrtDt }, () =>
|
||||||
dispatch(changeLocalSettings({ watchRecord: {} }))
|
dispatch(changeLocalSettings({ watchRecord: {} }))
|
||||||
@@ -596,7 +598,7 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props
|
|||||||
) {
|
) {
|
||||||
let watchStrtDt = formatGMTString(new Date());
|
let watchStrtDt = formatGMTString(new Date());
|
||||||
|
|
||||||
watchInterval.current = setInterval(() => {
|
watchIntervalVod.current = setInterval(() => {
|
||||||
let watchEndDt = formatGMTString(new Date());
|
let watchEndDt = formatGMTString(new Date());
|
||||||
let watchRecord = {
|
let watchRecord = {
|
||||||
...vodLogParamsRef.current,
|
...vodLogParamsRef.current,
|
||||||
@@ -611,7 +613,7 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props
|
|||||||
...status,
|
...status,
|
||||||
isModalVodLogReady: false,
|
isModalVodLogReady: false,
|
||||||
}));
|
}));
|
||||||
clearInterval(watchInterval.current);
|
clearInterval(watchIntervalVod.current);
|
||||||
dispatch(
|
dispatch(
|
||||||
sendLogVOD({ ...vodLogParamsRef.current, watchStrtDt }, () =>
|
sendLogVOD({ ...vodLogParamsRef.current, watchStrtDt }, () =>
|
||||||
dispatch(changeLocalSettings({ watchRecord: {} }))
|
dispatch(changeLocalSettings({ watchRecord: {} }))
|
||||||
@@ -629,7 +631,7 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props
|
|||||||
) {
|
) {
|
||||||
let watchStrtDt = formatGMTString(new Date());
|
let watchStrtDt = formatGMTString(new Date());
|
||||||
|
|
||||||
watchInterval.current = setInterval(() => {
|
watchIntervalVod.current = setInterval(() => {
|
||||||
let watchEndDt = formatGMTString(new Date());
|
let watchEndDt = formatGMTString(new Date());
|
||||||
let watchRecord = {
|
let watchRecord = {
|
||||||
...vodLogParamsRef.current,
|
...vodLogParamsRef.current,
|
||||||
@@ -644,7 +646,7 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props
|
|||||||
...status,
|
...status,
|
||||||
isFullVodLogReady: false,
|
isFullVodLogReady: false,
|
||||||
}));
|
}));
|
||||||
clearInterval(watchInterval.current);
|
clearInterval(watchIntervalVod.current);
|
||||||
dispatch(
|
dispatch(
|
||||||
sendLogVOD({ ...vodLogParamsRef.current, watchStrtDt }, () =>
|
sendLogVOD({ ...vodLogParamsRef.current, watchStrtDt }, () =>
|
||||||
dispatch(changeLocalSettings({ watchRecord: {} }))
|
dispatch(changeLocalSettings({ watchRecord: {} }))
|
||||||
@@ -662,7 +664,7 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props
|
|||||||
) {
|
) {
|
||||||
let watchStrtDt = formatGMTString(new Date());
|
let watchStrtDt = formatGMTString(new Date());
|
||||||
|
|
||||||
watchInterval.current = setInterval(() => {
|
watchIntervalVod.current = setInterval(() => {
|
||||||
let watchEndDt = formatGMTString(new Date());
|
let watchEndDt = formatGMTString(new Date());
|
||||||
let watchRecord = {
|
let watchRecord = {
|
||||||
...vodLogParamsRef.current,
|
...vodLogParamsRef.current,
|
||||||
@@ -677,7 +679,7 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props
|
|||||||
...status,
|
...status,
|
||||||
isDetailVodLogReady: false,
|
isDetailVodLogReady: false,
|
||||||
}));
|
}));
|
||||||
clearInterval(watchInterval.current);
|
clearInterval(watchIntervalVod.current);
|
||||||
dispatch(
|
dispatch(
|
||||||
sendLogVOD({ ...vodLogParamsRef.current, watchStrtDt }, () =>
|
sendLogVOD({ ...vodLogParamsRef.current, watchStrtDt }, () =>
|
||||||
dispatch(changeLocalSettings({ watchRecord: {} }))
|
dispatch(changeLocalSettings({ watchRecord: {} }))
|
||||||
@@ -752,7 +754,7 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props
|
|||||||
if (logStatus.isModalMediaLogReady && panelInfo?.modal) {
|
if (logStatus.isModalMediaLogReady && panelInfo?.modal) {
|
||||||
let watchStrtDt = formatGMTString(new Date());
|
let watchStrtDt = formatGMTString(new Date());
|
||||||
|
|
||||||
watchInterval.current = setInterval(() => {
|
watchIntervalMedia.current = setInterval(() => {
|
||||||
let watchEndDt = formatGMTString(new Date());
|
let watchEndDt = formatGMTString(new Date());
|
||||||
let watchRecord = {
|
let watchRecord = {
|
||||||
...mediaLogParamsRef.current,
|
...mediaLogParamsRef.current,
|
||||||
@@ -767,7 +769,7 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props
|
|||||||
...status,
|
...status,
|
||||||
isModalMediaLogReady: false,
|
isModalMediaLogReady: false,
|
||||||
}));
|
}));
|
||||||
clearInterval(watchInterval.current);
|
clearInterval(watchIntervalMedia.current);
|
||||||
dispatch(
|
dispatch(
|
||||||
sendLogVOD({ ...mediaLogParamsRef.current, watchStrtDt }, () =>
|
sendLogVOD({ ...mediaLogParamsRef.current, watchStrtDt }, () =>
|
||||||
dispatch(changeLocalSettings({ watchRecord: {} }))
|
dispatch(changeLocalSettings({ watchRecord: {} }))
|
||||||
@@ -781,7 +783,7 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props
|
|||||||
if (logStatus.isFullMediaLogReady && !panelInfo?.modal) {
|
if (logStatus.isFullMediaLogReady && !panelInfo?.modal) {
|
||||||
let watchStrtDt = formatGMTString(new Date());
|
let watchStrtDt = formatGMTString(new Date());
|
||||||
|
|
||||||
watchInterval.current = setInterval(() => {
|
watchIntervalMedia.current = setInterval(() => {
|
||||||
let watchEndDt = formatGMTString(new Date());
|
let watchEndDt = formatGMTString(new Date());
|
||||||
let watchRecord = {
|
let watchRecord = {
|
||||||
...mediaLogParamsRef.current,
|
...mediaLogParamsRef.current,
|
||||||
@@ -796,7 +798,7 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props
|
|||||||
...status,
|
...status,
|
||||||
isFullMediaLogReady: false,
|
isFullMediaLogReady: false,
|
||||||
}));
|
}));
|
||||||
clearInterval(watchInterval.current);
|
clearInterval(watchIntervalMedia.current);
|
||||||
dispatch(
|
dispatch(
|
||||||
sendLogVOD({ ...mediaLogParamsRef.current, watchStrtDt }, () =>
|
sendLogVOD({ ...mediaLogParamsRef.current, watchStrtDt }, () =>
|
||||||
dispatch(changeLocalSettings({ watchRecord: {} }))
|
dispatch(changeLocalSettings({ watchRecord: {} }))
|
||||||
|
|||||||
@@ -1,21 +1,21 @@
|
|||||||
import React, { useCallback, useEffect } from "react";
|
import React, { useCallback, useEffect } from 'react';
|
||||||
|
|
||||||
import classNames from "classnames";
|
import classNames from 'classnames';
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
|
|
||||||
import Spotlight from "@enact/spotlight";
|
import Spotlight from '@enact/spotlight';
|
||||||
import SpotlightContainerDecorator from "@enact/spotlight/SpotlightContainerDecorator";
|
import SpotlightContainerDecorator from '@enact/spotlight/SpotlightContainerDecorator';
|
||||||
import Spottable from "@enact/spotlight/Spottable";
|
import Spottable from '@enact/spotlight/Spottable';
|
||||||
|
|
||||||
import { updatePanel } from "../../../../actions/panelActions";
|
import { updatePanel } from '../../../../actions/panelActions';
|
||||||
import { panel_names } from "../../../../utils/Config";
|
import { panel_names } from '../../../../utils/Config';
|
||||||
import { SpotlightIds } from "../../../../utils/SpotlightIds";
|
import { SpotlightIds } from '../../../../utils/SpotlightIds';
|
||||||
import css from "./PlayerTabButton.module.less";
|
import css from './PlayerTabButton.module.less';
|
||||||
|
|
||||||
const SpottableComponent = Spottable("button");
|
const SpottableComponent = Spottable('button');
|
||||||
const Container = SpotlightContainerDecorator(
|
const Container = SpotlightContainerDecorator(
|
||||||
{ enterTo: "default-element", preserveld: true },
|
{ enterTo: 'default-element', preserveld: true },
|
||||||
"div"
|
'div'
|
||||||
);
|
);
|
||||||
export default function PlayerTabButton({
|
export default function PlayerTabButton({
|
||||||
sideContentsVisible,
|
sideContentsVisible,
|
||||||
@@ -26,7 +26,8 @@ export default function PlayerTabButton({
|
|||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const countryCode = useSelector((state) => state.common.httpHeader.cntry_cd);
|
const countryCode = useSelector((state) => state.common.httpHeader.cntry_cd);
|
||||||
|
|
||||||
const handleTabOnClick = (e) => {
|
const handleTabOnClick = useCallback(
|
||||||
|
(e) => {
|
||||||
dispatch(
|
dispatch(
|
||||||
updatePanel({
|
updatePanel({
|
||||||
name: panel_names.PLAYER_PANEL,
|
name: panel_names.PLAYER_PANEL,
|
||||||
@@ -36,12 +37,16 @@ export default function PlayerTabButton({
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
setSideContentsVisible((prev) => !prev);
|
setSideContentsVisible((prev) => !prev);
|
||||||
setTimeout(() => {
|
|
||||||
|
// microtask 큐를 사용하여 상태 업데이트 이후 포커스 이동
|
||||||
|
Promise.resolve().then(() => {
|
||||||
Spotlight.focus(SpotlightIds.PLAYER_TAB_BUTTON);
|
Spotlight.focus(SpotlightIds.PLAYER_TAB_BUTTON);
|
||||||
}, 0);
|
});
|
||||||
|
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
};
|
},
|
||||||
|
[dispatch, setSideContentsVisible]
|
||||||
|
);
|
||||||
|
|
||||||
const _onSpotlightLeft = useCallback(
|
const _onSpotlightLeft = useCallback(
|
||||||
(e) => {
|
(e) => {
|
||||||
@@ -52,7 +57,7 @@ export default function PlayerTabButton({
|
|||||||
}
|
}
|
||||||
if (!sideContentsVisible) {
|
if (!sideContentsVisible) {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
Spotlight.focus("videoIndicator-up-button");
|
Spotlight.focus('videoIndicator-up-button');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[sideContentsVisible]
|
[sideContentsVisible]
|
||||||
@@ -60,10 +65,10 @@ export default function PlayerTabButton({
|
|||||||
|
|
||||||
const onSpotlightDown = useCallback(
|
const onSpotlightDown = useCallback(
|
||||||
(e) => {
|
(e) => {
|
||||||
if (countryCode !== "US") {
|
if (countryCode !== 'US') {
|
||||||
if (videoType === "VOD") {
|
if (videoType === 'VOD') {
|
||||||
Spotlight.focus("videoIndicator-up-button");
|
Spotlight.focus('videoIndicator-up-button');
|
||||||
} else if (videoType === "LIVE") {
|
} else if (videoType === 'LIVE') {
|
||||||
Spotlight.focus(SpotlightIds.PLAYER_SLIDER);
|
Spotlight.focus(SpotlightIds.PLAYER_SLIDER);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@@ -80,7 +85,7 @@ export default function PlayerTabButton({
|
|||||||
(e) => {
|
(e) => {
|
||||||
if (!sideContentsVisible) {
|
if (!sideContentsVisible) {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
Spotlight.focus("videoIndicator-up-button");
|
Spotlight.focus('videoIndicator-up-button');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[sideContentsVisible]
|
[sideContentsVisible]
|
||||||
|
|||||||
@@ -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 defaultImage from '../../../../../assets/images/img-thumb-empty-144@3x.png';
|
||||||
import { updatePanel } from "../../../../actions/panelActions";
|
import { updatePanel } from '../../../../actions/panelActions';
|
||||||
import TVirtualGridList from "../../../../components/TVirtualGridList/TVirtualGridList";
|
import TVirtualGridList from '../../../../components/TVirtualGridList/TVirtualGridList';
|
||||||
import { LOG_CONTEXT_NAME, LOG_MENU, LOG_MESSAGE_ID, panel_names } from "../../../../utils/Config";
|
import { LOG_CONTEXT_NAME, LOG_MENU, LOG_MESSAGE_ID, panel_names } from '../../../../utils/Config';
|
||||||
import { $L, removeSpecificTags } from "../../../../utils/helperMethods";
|
import { $L, removeSpecificTags } from '../../../../utils/helperMethods';
|
||||||
import PlayerItemCard, { TYPES } from "../../PlayerItemCard/PlayerItemCard";
|
import PlayerItemCard, { TYPES } from '../../PlayerItemCard/PlayerItemCard';
|
||||||
import ListEmptyContents from "../TabContents/ListEmptyContents/ListEmptyContents";
|
import ListEmptyContents from '../TabContents/ListEmptyContents/ListEmptyContents';
|
||||||
import css from "./LiveChannelContents.module.less";
|
import css from './LiveChannelContents.module.less';
|
||||||
import { getMainCategoryShowDetail } from "../../../../actions/mainActions";
|
import { getMainCategoryShowDetail } from '../../../../actions/mainActions';
|
||||||
import { sendLogTotalRecommend } from "../../../../actions/logActions";
|
import { sendLogTotalRecommend } from '../../../../actions/logActions';
|
||||||
|
|
||||||
export default function FeaturedShowContents({
|
export default function FeaturedShowContents({
|
||||||
featuredShowsInfos,
|
featuredShowsInfos,
|
||||||
@@ -25,10 +25,12 @@ export default function FeaturedShowContents({
|
|||||||
tabIndex,
|
tabIndex,
|
||||||
handleItemFocus,
|
handleItemFocus,
|
||||||
tabTitle,
|
tabTitle,
|
||||||
panelInfo
|
panelInfo,
|
||||||
}) {
|
}) {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const isClickBlocked = useRef(false);
|
const isClickBlocked = useRef(false);
|
||||||
|
const blockTimeoutRef = useRef(null);
|
||||||
|
|
||||||
const handleFocus = useCallback(
|
const handleFocus = useCallback(
|
||||||
() => () => {
|
() => () => {
|
||||||
if (handleItemFocus) {
|
if (handleItemFocus) {
|
||||||
@@ -61,8 +63,8 @@ export default function FeaturedShowContents({
|
|||||||
category: catNm,
|
category: catNm,
|
||||||
partner: patncNm,
|
partner: patncNm,
|
||||||
contextName: LOG_CONTEXT_NAME.SHOW,
|
contextName: LOG_CONTEXT_NAME.SHOW,
|
||||||
messageId: LOG_MESSAGE_ID.CONTENTCLICK
|
messageId: LOG_MESSAGE_ID.CONTENTCLICK,
|
||||||
}
|
};
|
||||||
dispatch(sendLogTotalRecommend(params));
|
dispatch(sendLogTotalRecommend(params));
|
||||||
//중복클릭방지
|
//중복클릭방지
|
||||||
if (isClickBlocked.current) {
|
if (isClickBlocked.current) {
|
||||||
@@ -70,8 +72,15 @@ export default function FeaturedShowContents({
|
|||||||
}
|
}
|
||||||
|
|
||||||
isClickBlocked.current = true;
|
isClickBlocked.current = true;
|
||||||
setTimeout(() => {
|
|
||||||
|
// 이전 타이머가 있으면 정리
|
||||||
|
if (blockTimeoutRef.current) {
|
||||||
|
clearTimeout(blockTimeoutRef.current);
|
||||||
|
}
|
||||||
|
|
||||||
|
blockTimeoutRef.current = setTimeout(() => {
|
||||||
isClickBlocked.current = false;
|
isClickBlocked.current = false;
|
||||||
|
blockTimeoutRef.current = null;
|
||||||
}, 600);
|
}, 600);
|
||||||
|
|
||||||
if (currentVideoShowId && currentVideoShowId === showId) {
|
if (currentVideoShowId && currentVideoShowId === showId) {
|
||||||
@@ -107,29 +116,30 @@ export default function FeaturedShowContents({
|
|||||||
spotlightId={`tabChannel-video-${index}`}
|
spotlightId={`tabChannel-video-${index}`}
|
||||||
videoVerticalVisible={videoVerticalVisible}
|
videoVerticalVisible={videoVerticalVisible}
|
||||||
selectedIndex={index}
|
selectedIndex={index}
|
||||||
currentVideoVisible={
|
currentVideoVisible={currentVideoShowId === featuredShowsInfos[index].showId}
|
||||||
currentVideoShowId === featuredShowsInfos[index].showId
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
[
|
[featuredShowsInfos, currentVideoInfo, currentVideoShowId, videoVerticalVisible, isClickBlocked]
|
||||||
featuredShowsInfos,
|
|
||||||
currentVideoInfo,
|
|
||||||
currentVideoShowId,
|
|
||||||
videoVerticalVisible,
|
|
||||||
isClickBlocked,
|
|
||||||
]
|
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const timer = setTimeout(() => {
|
const timer = setTimeout(() => {
|
||||||
Spotlight.focus("tabChannel-video-" + selectedIndex);
|
Spotlight.focus('tabChannel-video-' + selectedIndex);
|
||||||
});
|
});
|
||||||
|
|
||||||
return () => clearTimeout(timer);
|
return () => clearTimeout(timer);
|
||||||
}, [featuredShowsInfos]);
|
}, [featuredShowsInfos]);
|
||||||
|
|
||||||
|
// cleanup useEffect
|
||||||
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
if (blockTimeoutRef.current) {
|
||||||
|
clearTimeout(blockTimeoutRef.current);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className={css.container}>
|
<div className={css.container}>
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ export default function LiveChannelContents({
|
|||||||
}) {
|
}) {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const isClickBlocked = useRef(false);
|
const isClickBlocked = useRef(false);
|
||||||
|
const blockTimeoutRef = useRef(null);
|
||||||
|
|
||||||
const handleFocus = useCallback(
|
const handleFocus = useCallback(
|
||||||
() => () => {
|
() => () => {
|
||||||
if (handleItemFocus) {
|
if (handleItemFocus) {
|
||||||
@@ -75,8 +77,15 @@ export default function LiveChannelContents({
|
|||||||
}
|
}
|
||||||
|
|
||||||
isClickBlocked.current = true;
|
isClickBlocked.current = true;
|
||||||
setTimeout(() => {
|
|
||||||
|
// 이전 타이머가 있으면 정리
|
||||||
|
if (blockTimeoutRef.current) {
|
||||||
|
clearTimeout(blockTimeoutRef.current);
|
||||||
|
}
|
||||||
|
|
||||||
|
blockTimeoutRef.current = setTimeout(() => {
|
||||||
isClickBlocked.current = false;
|
isClickBlocked.current = false;
|
||||||
|
blockTimeoutRef.current = null;
|
||||||
}, 600);
|
}, 600);
|
||||||
|
|
||||||
if (!showId) return;
|
if (!showId) return;
|
||||||
@@ -144,6 +153,15 @@ export default function LiveChannelContents({
|
|||||||
|
|
||||||
const containerClass = version === 2 ? cssV2.container : css.container;
|
const containerClass = version === 2 ? cssV2.container : css.container;
|
||||||
|
|
||||||
|
// cleanup useEffect
|
||||||
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
if (blockTimeoutRef.current) {
|
||||||
|
clearTimeout(blockTimeoutRef.current);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className={containerClass}>
|
<div className={containerClass}>
|
||||||
|
|||||||
@@ -120,9 +120,11 @@ export default function TabContainerV2({
|
|||||||
// tabIndex가 2(ShopNowButton)로 변경되면 자동으로 포커스 이동
|
// tabIndex가 2(ShopNowButton)로 변경되면 자동으로 포커스 이동
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (tabIndex === 2) {
|
if (tabIndex === 2) {
|
||||||
setTimeout(() => {
|
const timeoutId = setTimeout(() => {
|
||||||
Spotlight.focus('below-tab-shop-now-button');
|
Spotlight.focus('below-tab-shop-now-button');
|
||||||
}, 100);
|
}, 100);
|
||||||
|
|
||||||
|
return () => clearTimeout(timeoutId);
|
||||||
}
|
}
|
||||||
}, [tabIndex]);
|
}, [tabIndex]);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user