[251124] fix: PlayerPanel videoState updated
🕐 커밋 시간: 2025. 11. 24. 10:45:28 📊 변경 통계: • 총 파일: 9개 • 추가: +509줄 • 삭제: -66줄 📝 수정된 파일: ~ com.twin.app.shoptime/src/actions/mainActions.js ~ com.twin.app.shoptime/src/actions/playActions.js ~ com.twin.app.shoptime/src/api/TAxios.js ~ com.twin.app.shoptime/src/components/VideoPlayer/VideoPlayer.js ~ com.twin.app.shoptime/src/reducers/playReducer.js ~ com.twin.app.shoptime/src/views/HomePanel/HomeBanner/RandomUnit.jsx ~ com.twin.app.shoptime/src/views/HomePanel/HomeBanner/RollingUnit.jsx ~ com.twin.app.shoptime/src/views/HomePanel/HomePanel.jsx ~ com.twin.app.shoptime/src/views/PlayerPanel/PlayerPanel.jsx 🔧 함수 변경 내용: 📄 com.twin.app.shoptime/src/actions/mainActions.js (javascript): ❌ Deleted: onSuccess(), onFail() 📄 com.twin.app.shoptime/src/actions/playActions.js (javascript): 🔄 Modified: clearAllVideoTimers(), pauseModalVideo(), hideModalVideo() ❌ Deleted: CLEAR_PLAYER_INFO() 📄 com.twin.app.shoptime/src/api/TAxios.js (javascript): 🔄 Modified: setTokenRefreshing(), createSafeApiThunk() 📄 com.twin.app.shoptime/src/views/HomePanel/HomeBanner/RandomUnit.jsx (javascript): 🔄 Modified: SpotlightContainerDecorator() 📄 com.twin.app.shoptime/src/views/HomePanel/HomeBanner/RollingUnit.jsx (javascript): 🔄 Modified: createPanelInfo() 🔧 주요 변경 내용: • 핵심 비즈니스 로직 개선 • API 서비스 레이어 개선 • UI 컴포넌트 아키텍처 개선
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
import { URLS } from '../api/apiConfig';
|
import { URLS } from '../api/apiConfig';
|
||||||
import { TAxios } from '../api/TAxios';
|
import { TAxios, TAxiosAdvancedPromise } from '../api/TAxios';
|
||||||
import { convertUtcToLocal } from '../components/MediaPlayer/util';
|
import { convertUtcToLocal } from '../components/MediaPlayer/util';
|
||||||
import { CATEGORY_DATA_MAX_RESULTS_LIMIT, LOG_CONTEXT_NAME, LOG_MESSAGE_ID } from '../utils/Config';
|
import { CATEGORY_DATA_MAX_RESULTS_LIMIT, LOG_CONTEXT_NAME, LOG_MESSAGE_ID } from '../utils/Config';
|
||||||
import * as HelperMethods from '../utils/helperMethods';
|
import * as HelperMethods from '../utils/helperMethods';
|
||||||
@@ -464,29 +464,29 @@ export const getHomeFullVideoInfo =
|
|||||||
export const getMainLiveShowNowProduct =
|
export const getMainLiveShowNowProduct =
|
||||||
({ patnrId, showId, lstChgDt }) =>
|
({ patnrId, showId, lstChgDt }) =>
|
||||||
(dispatch, getState) => {
|
(dispatch, getState) => {
|
||||||
const onSuccess = (response) => {
|
return TAxiosAdvancedPromise(
|
||||||
// console.log('getMainLiveShowNowProduct onSuccess', response.data);
|
|
||||||
|
|
||||||
dispatch({
|
|
||||||
type: types.GET_MAIN_LIVE_SHOW_NOW_PRODUCT,
|
|
||||||
payload: response.data.data,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const onFail = (error) => {
|
|
||||||
console.error('getMainLiveShowNowProduct onFail', error);
|
|
||||||
};
|
|
||||||
|
|
||||||
TAxios(
|
|
||||||
dispatch,
|
dispatch,
|
||||||
getState,
|
getState,
|
||||||
'get',
|
'get',
|
||||||
URLS.GET_MAIN_LIVE_SHOW_NOW_PRODUCT,
|
URLS.GET_MAIN_LIVE_SHOW_NOW_PRODUCT,
|
||||||
{ patnrId, showId, lstChgDt },
|
{ patnrId, showId, lstChgDt },
|
||||||
{},
|
{},
|
||||||
onSuccess,
|
{
|
||||||
onFail
|
retries: 2, // 3회까지 재시도 (처음 시도 + 2회 재시도)
|
||||||
);
|
retryDelay: 500, // 500ms 간격으로 재시도
|
||||||
|
throwOnError: false, // 에러를 throw하지 않고 객체로 반환
|
||||||
|
}
|
||||||
|
).then((result) => {
|
||||||
|
if (result.success && result.data?.data) {
|
||||||
|
dispatch({
|
||||||
|
type: types.GET_MAIN_LIVE_SHOW_NOW_PRODUCT,
|
||||||
|
payload: result.data.data,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.error('getMainLiveShowNowProduct onFail', result.error);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const clearShopNowInfo = () => {
|
export const clearShopNowInfo = () => {
|
||||||
|
|||||||
@@ -68,24 +68,43 @@ export const startVideoPlayer =
|
|||||||
...rest
|
...rest
|
||||||
}) =>
|
}) =>
|
||||||
(dispatch, getState) => {
|
(dispatch, getState) => {
|
||||||
console.log('[startVideoPlayer] ✅ START - videoId:', videoId, ', showUrl:', showUrl, ', modal:', modal);
|
console.log(
|
||||||
|
'[startVideoPlayer] ✅ START - videoId:',
|
||||||
|
videoId,
|
||||||
|
', showUrl:',
|
||||||
|
showUrl,
|
||||||
|
', modal:',
|
||||||
|
modal
|
||||||
|
);
|
||||||
|
|
||||||
// 🔽 [251116] 즉시 로딩 상태 설정
|
// 🔽 [251116] 즉시 로딩 상태 설정
|
||||||
const videoIdentifier = videoId || showUrl;
|
const videoIdentifier = videoId || showUrl;
|
||||||
if (videoIdentifier) {
|
if (videoIdentifier) {
|
||||||
const displayMode = modal ? DISPLAY_STATUS.VISIBLE : DISPLAY_STATUS.FULLSCREEN;
|
const displayMode = modal ? DISPLAY_STATUS.VISIBLE : DISPLAY_STATUS.FULLSCREEN;
|
||||||
console.log('[startVideoPlayer] 📌 Setting playback loading - identifier:', videoIdentifier, ', displayMode:', displayMode);
|
console.log(
|
||||||
|
'[startVideoPlayer] 📌 Setting playback loading - identifier:',
|
||||||
|
videoIdentifier,
|
||||||
|
', displayMode:',
|
||||||
|
displayMode
|
||||||
|
);
|
||||||
dispatch(setPlaybackLoading(videoIdentifier, displayMode));
|
dispatch(setPlaybackLoading(videoIdentifier, displayMode));
|
||||||
} else {
|
} else {
|
||||||
console.log('[startVideoPlayer] ⚠️ No videoIdentifier provided (videoId and showUrl are both missing)');
|
console.log(
|
||||||
|
'[startVideoPlayer] ⚠️ No videoIdentifier provided (videoId and showUrl are both missing)'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const panels = getState().panels.panels;
|
const panels = getState().panels.panels;
|
||||||
const topPanel = panels[panels.length - 1];
|
const topPanel = panels[panels.length - 1];
|
||||||
let panelWorkingAction = pushPanel;
|
let panelWorkingAction = pushPanel;
|
||||||
|
|
||||||
const panelName = panel_names.PLAYER_PANEL;
|
const panelName = panel_names.PLAYER_PANEL;
|
||||||
console.log('[startVideoPlayer] 📊 Panel state - panelsCount:', panels.length, ', topPanelName:', topPanel?.name);
|
console.log(
|
||||||
|
'[startVideoPlayer] 📊 Panel state - panelsCount:',
|
||||||
|
panels.length,
|
||||||
|
', topPanelName:',
|
||||||
|
topPanel?.name
|
||||||
|
);
|
||||||
|
|
||||||
if (topPanel && topPanel.name === panelName) {
|
if (topPanel && topPanel.name === panelName) {
|
||||||
panelWorkingAction = updatePanel;
|
panelWorkingAction = updatePanel;
|
||||||
@@ -161,13 +180,27 @@ export const startVideoPlayerNew =
|
|||||||
...rest
|
...rest
|
||||||
}) =>
|
}) =>
|
||||||
(dispatch, getState) => {
|
(dispatch, getState) => {
|
||||||
console.log('[startVideoPlayerNew] *** ✅ START - bannerId:', bannerId, ', videoId:', videoId, ', showUrl:', showUrl, ', modal:', modal);
|
console.log(
|
||||||
|
'[startVideoPlayerNew] *** ✅ START - bannerId:',
|
||||||
|
bannerId,
|
||||||
|
', videoId:',
|
||||||
|
videoId,
|
||||||
|
', showUrl:',
|
||||||
|
showUrl,
|
||||||
|
', modal:',
|
||||||
|
modal
|
||||||
|
);
|
||||||
|
|
||||||
// 🔽 [251116] 즉시 로딩 상태 설정
|
// 🔽 [251116] 즉시 로딩 상태 설정
|
||||||
const videoIdentifier = videoId || showUrl || bannerId;
|
const videoIdentifier = videoId || showUrl || bannerId;
|
||||||
if (videoIdentifier) {
|
if (videoIdentifier) {
|
||||||
const displayMode = modal ? DISPLAY_STATUS.VISIBLE : DISPLAY_STATUS.FULLSCREEN;
|
const displayMode = modal ? DISPLAY_STATUS.VISIBLE : DISPLAY_STATUS.FULLSCREEN;
|
||||||
console.log('[startVideoPlayerNew] *** 📌 Setting playback loading - identifier:', videoIdentifier, ', displayMode:', displayMode);
|
console.log(
|
||||||
|
'[startVideoPlayerNew] *** 📌 Setting playback loading - identifier:',
|
||||||
|
videoIdentifier,
|
||||||
|
', displayMode:',
|
||||||
|
displayMode
|
||||||
|
);
|
||||||
dispatch(setPlaybackLoading(videoIdentifier, displayMode));
|
dispatch(setPlaybackLoading(videoIdentifier, displayMode));
|
||||||
} else {
|
} else {
|
||||||
console.log('[startVideoPlayerNew] *** ⚠️ No videoIdentifier provided');
|
console.log('[startVideoPlayerNew] *** ⚠️ No videoIdentifier provided');
|
||||||
@@ -179,11 +212,19 @@ export const startVideoPlayerNew =
|
|||||||
|
|
||||||
// const panelName = useNewPlayer ? panel_names.PLAYER_PANEL_NEW : panel_names.PLAYER_PANEL;
|
// const panelName = useNewPlayer ? panel_names.PLAYER_PANEL_NEW : panel_names.PLAYER_PANEL;
|
||||||
const panelName = panel_names.PLAYER_PANEL;
|
const panelName = panel_names.PLAYER_PANEL;
|
||||||
console.log('[startVideoPlayerNew] *** 📊 Panel state - panelsCount:', panels.length, ', topPanelName:', topPanel?.name);
|
console.log(
|
||||||
|
'[startVideoPlayerNew] *** 📊 Panel state - panelsCount:',
|
||||||
|
panels.length,
|
||||||
|
', topPanelName:',
|
||||||
|
topPanel?.name
|
||||||
|
);
|
||||||
|
|
||||||
if (topPanel && topPanel.name === panelName) {
|
if (topPanel && topPanel.name === panelName) {
|
||||||
panelWorkingAction = updatePanel;
|
panelWorkingAction = updatePanel;
|
||||||
console.log('[startVideoPlayerNew] *** 📋 Current PLAYER_PANEL panelInfo:', topPanel.panelInfo);
|
console.log(
|
||||||
|
'[startVideoPlayerNew] *** 📋 Current PLAYER_PANEL panelInfo:',
|
||||||
|
topPanel.panelInfo
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 중복 실행 방지: 같은 배너 + 같은 modal 상태/컨테이너 + 같은 URL이면 skip
|
// 중복 실행 방지: 같은 배너 + 같은 modal 상태/컨테이너 + 같은 URL이면 skip
|
||||||
@@ -195,7 +236,18 @@ export const startVideoPlayerNew =
|
|||||||
const isSameShowUrl = currentPanelInfo.showUrl === showUrl;
|
const isSameShowUrl = currentPanelInfo.showUrl === showUrl;
|
||||||
const isSameVideoId = currentPanelInfo.videoId === videoId;
|
const isSameVideoId = currentPanelInfo.videoId === videoId;
|
||||||
|
|
||||||
console.log('[startVideoPlayerNew] *** 🔍 Duplicate check - isSameBanner:', isSameBanner, ', isSameModalType:', isSameModalType, ', isSameContainer:', isSameContainer, ', isSameShowUrl:', isSameShowUrl, ', isSameVideoId:', isSameVideoId);
|
console.log(
|
||||||
|
'[startVideoPlayerNew] *** 🔍 Duplicate check - isSameBanner:',
|
||||||
|
isSameBanner,
|
||||||
|
', isSameModalType:',
|
||||||
|
isSameModalType,
|
||||||
|
', isSameContainer:',
|
||||||
|
isSameContainer,
|
||||||
|
', isSameShowUrl:',
|
||||||
|
isSameShowUrl,
|
||||||
|
', isSameVideoId:',
|
||||||
|
isSameVideoId
|
||||||
|
);
|
||||||
|
|
||||||
if (isSameBanner && isSameModalType && isSameContainer && isSameShowUrl && isSameVideoId) {
|
if (isSameBanner && isSameModalType && isSameContainer && isSameShowUrl && isSameVideoId) {
|
||||||
console.log('[startVideoPlayerNew] *** ⏭️ SKIPPED - 동일한 요청', {
|
console.log('[startVideoPlayerNew] *** ⏭️ SKIPPED - 동일한 요청', {
|
||||||
@@ -232,7 +284,10 @@ export const startVideoPlayerNew =
|
|||||||
true
|
true
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
console.log('[startVideoPlayerNew] *** ✨ Panel action dispatched - action:', panelWorkingAction === updatePanel ? 'updatePanel' : 'pushPanel');
|
console.log(
|
||||||
|
'[startVideoPlayerNew] *** ✨ Panel action dispatched - action:',
|
||||||
|
panelWorkingAction === updatePanel ? 'updatePanel' : 'pushPanel'
|
||||||
|
);
|
||||||
|
|
||||||
// [COMMENTED OUT] 비디오 재생 시 강제 포커스 이동 비활성화
|
// [COMMENTED OUT] 비디오 재생 시 강제 포커스 이동 비활성화
|
||||||
// if (modal && modalContainerId && !spotlightDisable) {
|
// if (modal && modalContainerId && !spotlightDisable) {
|
||||||
@@ -324,7 +379,7 @@ export const pauseModalVideo = () => (dispatch, getState) => {
|
|||||||
// 모달 비디오를 재생 (일시정지 해제)
|
// 모달 비디오를 재생 (일시정지 해제)
|
||||||
export const resumeModalVideo = () => (dispatch, getState) => {
|
export const resumeModalVideo = () => (dispatch, getState) => {
|
||||||
const panels = getState().panels.panels;
|
const panels = getState().panels.panels;
|
||||||
|
|
||||||
// modal PlayerPanel 찾기
|
// modal PlayerPanel 찾기
|
||||||
const modalPlayerPanel = panels.find(
|
const modalPlayerPanel = panels.find(
|
||||||
(panel) => panel.name === panel_names.PLAYER_PANEL && panel.panelInfo?.modal
|
(panel) => panel.name === panel_names.PLAYER_PANEL && panel.panelInfo?.modal
|
||||||
@@ -498,7 +553,10 @@ export const showModalVideo = () => (dispatch, getState) => {
|
|||||||
skipModalStyleRecalculation: false, // 위치 변경 시 DOM 기준으로 다시 계산하도록 허용
|
skipModalStyleRecalculation: false, // 위치 변경 시 DOM 기준으로 다시 계산하도록 허용
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log('[showModalVideo] *** 🔄 Updated panelInfo - shouldShrinkTo1px:', updatedPanelInfo.shouldShrinkTo1px);
|
console.log(
|
||||||
|
'[showModalVideo] *** 🔄 Updated panelInfo - shouldShrinkTo1px:',
|
||||||
|
updatedPanelInfo.shouldShrinkTo1px
|
||||||
|
);
|
||||||
console.log('[showModalVideo] *** 📍 Restored modalStyle:', updatedPanelInfo.modalStyle);
|
console.log('[showModalVideo] *** 📍 Restored modalStyle:', updatedPanelInfo.modalStyle);
|
||||||
|
|
||||||
dispatch(
|
dispatch(
|
||||||
@@ -699,10 +757,27 @@ export const CLEAR_PLAYER_INFO = () => ({
|
|||||||
* @param {number} playState.duration - 전체 비디오 길이(초)
|
* @param {number} playState.duration - 전체 비디오 길이(초)
|
||||||
* @param {number} playState.playbackRate - 재생 속도
|
* @param {number} playState.playbackRate - 재생 속도
|
||||||
*/
|
*/
|
||||||
export const updateVideoPlayState = (playState) => ({
|
export const updateVideoPlayState = (playState) => (dispatch, getState) => {
|
||||||
type: types.UPDATE_VIDEO_PLAY_STATE,
|
const currentState = getState().play.videoPlayState;
|
||||||
payload: playState,
|
|
||||||
});
|
// 상태 변화 감지
|
||||||
|
const hasChanges = Object.keys(playState).some((key) => {
|
||||||
|
return currentState[key] !== playState[key];
|
||||||
|
});
|
||||||
|
|
||||||
|
if (hasChanges) {
|
||||||
|
console.log('🔄 [Redux] updateVideoPlayState action created', {
|
||||||
|
...playState,
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
caller: new Error().stack?.split('\n')[2]?.trim() || 'unknown',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: types.UPDATE_VIDEO_PLAY_STATE,
|
||||||
|
payload: playState,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
/* 🔽 [추가] 새로운 '플레이 제어 매니저' 액션들 */
|
/* 🔽 [추가] 새로운 '플레이 제어 매니저' 액션들 */
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ export const setTokenRefreshing = (value) => {
|
|||||||
tokenRefreshing = value;
|
tokenRefreshing = value;
|
||||||
};
|
};
|
||||||
export const runDelayedAction = (dispatch, getState) => {
|
export const runDelayedAction = (dispatch, getState) => {
|
||||||
console.log('runDelayedAction axiosQueue size', axiosQueue.length);
|
// console.log('runDelayedAction axiosQueue size', axiosQueue.length);
|
||||||
while (axiosQueue.length > 0) {
|
while (axiosQueue.length > 0) {
|
||||||
const requestConfig = axiosQueue.shift(); // queue에서 요청을 하나씩 shift
|
const requestConfig = axiosQueue.shift(); // queue에서 요청을 하나씩 shift
|
||||||
TAxios(
|
TAxios(
|
||||||
@@ -309,7 +309,7 @@ export const TAxiosAdvancedPromise = (
|
|||||||
|
|
||||||
const attemptRequest = () => {
|
const attemptRequest = () => {
|
||||||
attempts++;
|
attempts++;
|
||||||
console.log(`TAxiosPromise attempt ${attempts}/${maxAttempts} for ${baseUrl}`);
|
// console.log(`TAxiosPromise attempt ${attempts}/${maxAttempts} for ${baseUrl}`);
|
||||||
|
|
||||||
const timeoutId = setTimeout(() => {
|
const timeoutId = setTimeout(() => {
|
||||||
const timeoutError = new Error(`Request timeout after ${timeout}ms for ${baseUrl}`);
|
const timeoutError = new Error(`Request timeout after ${timeout}ms for ${baseUrl}`);
|
||||||
@@ -335,7 +335,7 @@ export const TAxiosAdvancedPromise = (
|
|||||||
// onSuccess
|
// onSuccess
|
||||||
(response) => {
|
(response) => {
|
||||||
clearTimeout(timeoutId);
|
clearTimeout(timeoutId);
|
||||||
console.log(`TAxiosPromise success on attempt ${attempts} for ${baseUrl}`);
|
// console.log(`TAxiosPromise success on attempt ${attempts} for ${baseUrl}`);
|
||||||
resolve({
|
resolve({
|
||||||
success: true,
|
success: true,
|
||||||
data: response.data,
|
data: response.data,
|
||||||
@@ -491,7 +491,7 @@ export const safeUsageExamples = {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
console.log('Success:', result.data);
|
// console.log('Success:', result.data);
|
||||||
return result.data;
|
return result.data;
|
||||||
} else {
|
} else {
|
||||||
console.error('API call failed:', result.error);
|
console.error('API call failed:', result.error);
|
||||||
@@ -534,7 +534,7 @@ export const safeUsageExamples = {
|
|||||||
const result = await TAxiosAll(requests);
|
const result = await TAxiosAll(requests);
|
||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
console.log('All requests succeeded');
|
// console.log('All requests succeeded');
|
||||||
return result.successResults.map((item) => item.result);
|
return result.successResults.map((item) => item.result);
|
||||||
} else {
|
} else {
|
||||||
console.error('Some requests failed:', result.failedResults);
|
console.error('Some requests failed:', result.failedResults);
|
||||||
@@ -562,7 +562,7 @@ export const ComponentUsageExample = () => {
|
|||||||
setLoading(false);
|
setLoading(false);
|
||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
console.log('Terms fetched successfully');
|
// console.log('Terms fetched successfully');
|
||||||
// 성공 처리 (예: 성공 토스트 표시)
|
// 성공 처리 (예: 성공 토스트 표시)
|
||||||
} else {
|
} else {
|
||||||
console.error('Failed to fetch terms:', result.message);
|
console.error('Failed to fetch terms:', result.message);
|
||||||
|
|||||||
@@ -725,6 +725,7 @@ const VideoPlayerBase = class extends React.Component {
|
|||||||
tabContainerVersion: PropTypes.number,
|
tabContainerVersion: PropTypes.number,
|
||||||
tabIndexV2: PropTypes.number,
|
tabIndexV2: PropTypes.number,
|
||||||
dispatch: PropTypes.func,
|
dispatch: PropTypes.func,
|
||||||
|
videoPlayState: PropTypes.object,
|
||||||
};
|
};
|
||||||
|
|
||||||
static contextType = FloatingLayerContext;
|
static contextType = FloatingLayerContext;
|
||||||
@@ -1546,17 +1547,101 @@ const VideoPlayerBase = class extends React.Component {
|
|||||||
}
|
}
|
||||||
this.setState(updatedState);
|
this.setState(updatedState);
|
||||||
|
|
||||||
// Redux에 비디오 재생 상태 업데이트
|
// Redux에 비디오 재생 상태 업데이트 (기존 로직 유지)
|
||||||
if (this.props.dispatch) {
|
if (this.props.dispatch) {
|
||||||
this.props.dispatch(
|
// 🔥 onProgress 이벤트는 Redux 업데이트하지 않음 (빈번한 이벤트)
|
||||||
updateVideoPlayState({
|
const shouldUpdateRedux = !['onProgress'].includes(ev.type);
|
||||||
|
|
||||||
|
if (shouldUpdateRedux) {
|
||||||
|
const updateState = {
|
||||||
isPlaying: !updatedState.paused,
|
isPlaying: !updatedState.paused,
|
||||||
isPaused: updatedState.paused,
|
isPaused: updatedState.paused,
|
||||||
currentTime: updatedState.currentTime,
|
currentTime: updatedState.currentTime,
|
||||||
duration: updatedState.duration,
|
duration: updatedState.duration,
|
||||||
playbackRate: updatedState.playbackRate,
|
playbackRate: updatedState.playbackRate,
|
||||||
})
|
};
|
||||||
);
|
|
||||||
|
// 가장 중요한 이벤트만 로그
|
||||||
|
const shouldLogEvent = ['play', 'pause', 'ended'].includes(ev.type);
|
||||||
|
if (shouldLogEvent) {
|
||||||
|
console.log('🔄 [PlayerPanel][VideoPlayer] Event-driven Redux update', {
|
||||||
|
eventType: ev.type,
|
||||||
|
videoState: updatedState,
|
||||||
|
updateState,
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 🔍 Redux dispatch 확인
|
||||||
|
console.log('📤 [PlayerPanel][VideoPlayer] Dispatching Redux update', {
|
||||||
|
eventType: ev.type,
|
||||||
|
updateState,
|
||||||
|
hasDispatch: !!this.props.dispatch,
|
||||||
|
propsVideoPlayState: this.props.videoPlayState,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.props.dispatch(updateVideoPlayState(updateState));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log('❌ [PlayerPanel][VideoPlayer] No dispatch prop available', {
|
||||||
|
props: Object.keys(this.props),
|
||||||
|
hasDispatch: !!this.props.dispatch,
|
||||||
|
hasVideoPlayState: !!this.props.videoPlayState,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 🔹 [강화] 내부 상태와 Redux 상태 동기화
|
||||||
|
// Redux 상태를 우선적으로 사용하여 내부 상태 일관성 확보
|
||||||
|
if (this.props.videoPlayState && typeof this.props.videoPlayState === 'object') {
|
||||||
|
// Redux 상태 디버깅 (최소한의 중요 이벤트만)
|
||||||
|
if (ev.type === 'play' || ev.type === 'pause') {
|
||||||
|
console.log('🔍 [PlayerPanel][VideoPlayer] Redux state debug', {
|
||||||
|
videoPlayState: this.props.videoPlayState,
|
||||||
|
isPaused: this.props.videoPlayState?.isPaused,
|
||||||
|
isPlaying: this.props.videoPlayState?.isPlaying,
|
||||||
|
currentTime: this.props.videoPlayState?.currentTime,
|
||||||
|
eventType: ev.type,
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const { currentTime, paused, playbackRate } = this.props.videoPlayState;
|
||||||
|
|
||||||
|
// Redux 상태와 현재 내부 상태가 크게 다를 경우 내부 상태 업데이트
|
||||||
|
const timeDiff = Math.abs(currentTime - this.state.currentTime);
|
||||||
|
const shouldUpdateTime = timeDiff > 0.5; // 0.5초 이상 차이 시 업데이트
|
||||||
|
|
||||||
|
// 빈번한 이벤트는 로그에서 제외
|
||||||
|
const isFrequentEvent = [
|
||||||
|
'onProgress',
|
||||||
|
'onBuffer',
|
||||||
|
'onBufferEnd',
|
||||||
|
'onReady',
|
||||||
|
'onDuration',
|
||||||
|
'onStart',
|
||||||
|
].includes(ev.type);
|
||||||
|
const hasSignificantChange =
|
||||||
|
shouldUpdateTime || (paused !== this.state.paused && !isFrequentEvent);
|
||||||
|
|
||||||
|
// 중요한 상태 변화가 있고 빈번한 이벤트가 아닐 때만 로그
|
||||||
|
if (hasSignificantChange && !isFrequentEvent) {
|
||||||
|
console.log('🔄 [PlayerPanel][VideoPlayer] Syncing internal state with Redux', {
|
||||||
|
timeDiff,
|
||||||
|
shouldUpdateTime,
|
||||||
|
pausedDiff: paused !== this.state.paused,
|
||||||
|
reduxPaused: paused,
|
||||||
|
internalPaused: this.state.paused,
|
||||||
|
eventType: ev.type,
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasSignificantChange) {
|
||||||
|
this.setState({
|
||||||
|
currentTime: shouldUpdateTime ? currentTime : this.state.currentTime,
|
||||||
|
paused: paused !== undefined ? paused : this.state.paused,
|
||||||
|
playbackRate: playbackRate !== undefined ? playbackRate : this.state.playbackRate,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1569,6 +1654,7 @@ const VideoPlayerBase = class extends React.Component {
|
|||||||
/**
|
/**
|
||||||
* Returns an object with the current state of the media including `currentTime`, `duration`,
|
* Returns an object with the current state of the media including `currentTime`, `duration`,
|
||||||
* `paused`, `playbackRate`, `proportionLoaded`, and `proportionPlayed`.
|
* `paused`, `playbackRate`, `proportionLoaded`, and `proportionPlayed`.
|
||||||
|
* Redux 상태와 내부 상태를 우선적으로 사용하여 일관성 보장
|
||||||
*
|
*
|
||||||
* @function
|
* @function
|
||||||
* @memberof sandstone/VideoPlayer.VideoPlayerBase.prototype
|
* @memberof sandstone/VideoPlayer.VideoPlayerBase.prototype
|
||||||
@@ -1576,13 +1662,19 @@ const VideoPlayerBase = class extends React.Component {
|
|||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
getMediaState = () => {
|
getMediaState = () => {
|
||||||
|
// Redux 상태를 우선적으로 사용하여 일관성 보장
|
||||||
|
// Redux 상태가 없으면 내부 상태 사용 (fallback)
|
||||||
|
const reduxState = this.props.videoPlayState;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
currentTime: this.state.currentTime,
|
currentTime: reduxState?.currentTime ?? this.state.currentTime,
|
||||||
duration: this.state.duration,
|
duration: reduxState?.duration ?? this.state.duration,
|
||||||
paused: this.state.paused,
|
paused: reduxState?.isPaused ?? this.state.paused,
|
||||||
playbackRate: this.video?.playbackRate,
|
playbackRate: reduxState?.playbackRate ?? this.video?.playbackRate ?? this.state.playbackRate,
|
||||||
proportionLoaded: this.state.proportionLoaded,
|
proportionLoaded: this.state.proportionLoaded,
|
||||||
proportionPlayed: this.state.proportionPlayed,
|
proportionPlayed: this.state.proportionPlayed,
|
||||||
|
// Redux 상태 정보도 포함
|
||||||
|
isPlaying: reduxState?.isPlaying ?? !this.state.paused,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1611,7 +1703,16 @@ const VideoPlayerBase = class extends React.Component {
|
|||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
play = () => {
|
play = () => {
|
||||||
|
console.log('🟢 [PlayerPanel][VideoPlayer] play() called', {
|
||||||
|
currentTime: this.state.currentTime,
|
||||||
|
duration: this.state.duration,
|
||||||
|
paused: this.state.paused,
|
||||||
|
sourceUnavailable: this.state.sourceUnavailable,
|
||||||
|
prevCommand: this.prevCommand,
|
||||||
|
});
|
||||||
|
|
||||||
if (this.state.sourceUnavailable) {
|
if (this.state.sourceUnavailable) {
|
||||||
|
console.log('⚠️ [PlayerPanel][VideoPlayer] play() aborted - source unavailable');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1623,6 +1724,19 @@ const VideoPlayerBase = class extends React.Component {
|
|||||||
this.send('play');
|
this.send('play');
|
||||||
this.announce($L('Play'));
|
this.announce($L('Play'));
|
||||||
this.startDelayedMiniFeedbackHide(5000);
|
this.startDelayedMiniFeedbackHide(5000);
|
||||||
|
|
||||||
|
// Redux 상태 업데이트 - 재생 상태로 변경
|
||||||
|
if (this.props.dispatch) {
|
||||||
|
this.props.dispatch(
|
||||||
|
updateVideoPlayState({
|
||||||
|
isPlaying: true,
|
||||||
|
isPaused: false,
|
||||||
|
currentTime: this.state.currentTime,
|
||||||
|
duration: this.state.duration,
|
||||||
|
playbackRate: 1,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1633,7 +1747,16 @@ const VideoPlayerBase = class extends React.Component {
|
|||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
pause = () => {
|
pause = () => {
|
||||||
|
console.log('🔴 [VideoPlayer] pause() called', {
|
||||||
|
currentTime: this.state.currentTime,
|
||||||
|
duration: this.state.duration,
|
||||||
|
paused: this.state.paused,
|
||||||
|
sourceUnavailable: this.state.sourceUnavailable,
|
||||||
|
prevCommand: this.prevCommand,
|
||||||
|
});
|
||||||
|
|
||||||
if (this.state.sourceUnavailable) {
|
if (this.state.sourceUnavailable) {
|
||||||
|
console.log('⚠️ [VideoPlayer] pause() aborted - source unavailable');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1645,6 +1768,22 @@ const VideoPlayerBase = class extends React.Component {
|
|||||||
this.send('pause');
|
this.send('pause');
|
||||||
this.announce($L('Pause'));
|
this.announce($L('Pause'));
|
||||||
this.stopDelayedMiniFeedbackHide();
|
this.stopDelayedMiniFeedbackHide();
|
||||||
|
|
||||||
|
// Redux 상태 업데이트 - 일시정지 상태로 변경
|
||||||
|
if (this.props.dispatch) {
|
||||||
|
const pauseState = {
|
||||||
|
isPlaying: false,
|
||||||
|
isPaused: true,
|
||||||
|
currentTime: this.state.currentTime,
|
||||||
|
duration: this.state.duration,
|
||||||
|
playbackRate: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log('📤 [VideoPlayer] Dispatching pause state', pauseState);
|
||||||
|
this.props.dispatch(updateVideoPlayState(pauseState));
|
||||||
|
} else {
|
||||||
|
console.log('⚠️ [VideoPlayer] No dispatch prop available - Redux state not updated');
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1656,6 +1795,15 @@ const VideoPlayerBase = class extends React.Component {
|
|||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
seek = (timeIndex) => {
|
seek = (timeIndex) => {
|
||||||
|
console.log('⏩ [VideoPlayer] seek() called', {
|
||||||
|
timeIndex,
|
||||||
|
currentTime: this.state.currentTime,
|
||||||
|
duration: this.state.duration,
|
||||||
|
videoDuration: this.video?.duration,
|
||||||
|
seekDisabled: this.props.seekDisabled,
|
||||||
|
sourceUnavailable: this.state.sourceUnavailable,
|
||||||
|
});
|
||||||
|
|
||||||
if (this.video) {
|
if (this.video) {
|
||||||
if (
|
if (
|
||||||
!this.props.seekDisabled &&
|
!this.props.seekDisabled &&
|
||||||
@@ -1663,14 +1811,37 @@ const VideoPlayerBase = class extends React.Component {
|
|||||||
!this.state.sourceUnavailable
|
!this.state.sourceUnavailable
|
||||||
) {
|
) {
|
||||||
// last time error
|
// last time error
|
||||||
if (timeIndex >= this.video.duration) {
|
const actualSeekTime =
|
||||||
this.video.currentTime = this.video.duration - 1;
|
timeIndex >= this.video.duration ? this.video.duration - 1 : timeIndex;
|
||||||
|
this.video.currentTime = actualSeekTime;
|
||||||
|
|
||||||
|
console.log('⏩ [VideoPlayer] Video seek completed', {
|
||||||
|
requestedTime: timeIndex,
|
||||||
|
actualTime: actualSeekTime,
|
||||||
|
videoDuration: this.video.duration,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Redux 상태 업데이트 - 시간 이동 상태 반영
|
||||||
|
if (this.props.dispatch) {
|
||||||
|
const seekState = {
|
||||||
|
isPlaying: !this.state.paused,
|
||||||
|
isPaused: this.state.paused,
|
||||||
|
currentTime: actualSeekTime,
|
||||||
|
duration: this.state.duration,
|
||||||
|
playbackRate: this.state.playbackRate,
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log('📤 [VideoPlayer] Dispatching seek state', seekState);
|
||||||
|
this.props.dispatch(updateVideoPlayState(seekState));
|
||||||
} else {
|
} else {
|
||||||
this.video.currentTime = timeIndex;
|
console.log('⚠️ [VideoPlayer] No dispatch prop available - Redux state not updated');
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
console.log('❌ [VideoPlayer] seek failed - disabled or source unavailable');
|
||||||
forward('onSeekFailed', {}, this.props);
|
forward('onSeekFailed', {}, this.props);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
console.log('❌ [VideoPlayer] seek failed - no video element');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -4,21 +4,52 @@ import { PLAYBACK_STATUS, DISPLAY_STATUS } from '../actions/playActions';
|
|||||||
const initialState = {
|
const initialState = {
|
||||||
subTitleBlobs: {},
|
subTitleBlobs: {},
|
||||||
chatData: null,
|
chatData: null,
|
||||||
videoPlayState: {
|
// 🔍 패널별 비디오 상태 분리
|
||||||
// 기존 상태들 유지
|
playerPanelVideoState: {
|
||||||
|
// PlayerPanel 전용 상태
|
||||||
isPlaying: false,
|
isPlaying: false,
|
||||||
isPaused: true,
|
isPaused: true,
|
||||||
currentTime: 0,
|
currentTime: 0,
|
||||||
duration: 0,
|
duration: 0,
|
||||||
playbackRate: 1,
|
playbackRate: 1,
|
||||||
|
|
||||||
// 🔽 [251116] 새로운 비디오 상태 관리 시스템
|
// [251116] 새로운 비디오 상태 관리 시스템
|
||||||
playback: PLAYBACK_STATUS.NOT_PLAYING, // 재생 상태
|
playback: PLAYBACK_STATUS.NOT_PLAYING,
|
||||||
display: DISPLAY_STATUS.HIDDEN, // 화면 표시 상태
|
display: DISPLAY_STATUS.HIDDEN,
|
||||||
videoId: null, // 현재 비디오 ID
|
videoId: null,
|
||||||
loadingProgress: 0, // 로딩 진행률 (0-100)
|
loadingProgress: 0,
|
||||||
loadingError: null, // 로딩 에러 정보
|
loadingError: null,
|
||||||
lastUpdate: null, // 마지막 업데이트 시간
|
lastUpdate: null,
|
||||||
|
},
|
||||||
|
detailPanelVideoState: {
|
||||||
|
// DetailPanel 전용 상태
|
||||||
|
isPlaying: false,
|
||||||
|
isPaused: true,
|
||||||
|
currentTime: 0,
|
||||||
|
duration: 0,
|
||||||
|
playbackRate: 1,
|
||||||
|
|
||||||
|
// [251116] 새로운 비디오 상태 관리 시스템
|
||||||
|
playback: PLAYBACK_STATUS.NOT_PLAYING,
|
||||||
|
display: DISPLAY_STATUS.HIDDEN,
|
||||||
|
videoId: null,
|
||||||
|
loadingProgress: 0,
|
||||||
|
loadingError: null,
|
||||||
|
lastUpdate: null,
|
||||||
|
},
|
||||||
|
// 기존 videoPlayState는 하위 호환성을 위해 유지
|
||||||
|
videoPlayState: {
|
||||||
|
isPlaying: false,
|
||||||
|
isPaused: true,
|
||||||
|
currentTime: 0,
|
||||||
|
duration: 0,
|
||||||
|
playbackRate: 1,
|
||||||
|
playback: PLAYBACK_STATUS.NOT_PLAYING,
|
||||||
|
display: DISPLAY_STATUS.HIDDEN,
|
||||||
|
videoId: null,
|
||||||
|
loadingProgress: 0,
|
||||||
|
loadingError: null,
|
||||||
|
lastUpdate: null,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -60,12 +91,35 @@ export const playReducer = (state = initialState, action) => {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
case types.UPDATE_VIDEO_PLAY_STATE: {
|
case types.UPDATE_VIDEO_PLAY_STATE: {
|
||||||
|
const newState = {
|
||||||
|
...state.videoPlayState,
|
||||||
|
...action.payload,
|
||||||
|
};
|
||||||
|
|
||||||
|
// 🔍 실제 상태 변화 감지 - 중요한 변화만 로깅
|
||||||
|
const importantKeys = ['isPaused', 'isPlaying', 'playback', 'display'];
|
||||||
|
const hasImportantChange = importantKeys.some((key) => {
|
||||||
|
return state.videoPlayState?.[key] !== newState[key];
|
||||||
|
});
|
||||||
|
|
||||||
|
if (hasImportantChange) {
|
||||||
|
console.log('🔄 [Redux Reducer] VIDEO PLAY STATE CRITICAL CHANGE', {
|
||||||
|
previousPaused: state.videoPlayState?.isPaused,
|
||||||
|
newPaused: newState.isPaused,
|
||||||
|
previousPlaying: state.videoPlayState?.isPlaying,
|
||||||
|
newPlaying: newState.isPlaying,
|
||||||
|
previousPlayback: state.videoPlayState?.playback,
|
||||||
|
newPlayback: newState.playback,
|
||||||
|
previousDisplay: state.videoPlayState?.display,
|
||||||
|
newDisplay: newState.display,
|
||||||
|
currentTime: newState.currentTime,
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
videoPlayState: {
|
videoPlayState: newState,
|
||||||
...state.videoPlayState,
|
|
||||||
...action.payload,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import liveShow from '../../../../assets/images/tag-liveshow.png';
|
|||||||
import { changeAppStatus } from '../../../actions/commonActions';
|
import { changeAppStatus } from '../../../actions/commonActions';
|
||||||
import { updateHomeInfo, setVideoTransitionLock } from '../../../actions/homeActions';
|
import { updateHomeInfo, setVideoTransitionLock } from '../../../actions/homeActions';
|
||||||
import { sendLogTopContents, sendLogTotalRecommend } from '../../../actions/logActions';
|
import { sendLogTopContents, sendLogTotalRecommend } from '../../../actions/logActions';
|
||||||
import { pushPanel, navigateFromRandomUnit } from '../../../actions/panelActions';
|
import { pushPanel, navigateFromRandomUnit, SOURCE_MENUS } from '../../../actions/panelActions';
|
||||||
import {
|
import {
|
||||||
finishVideoPreview,
|
finishVideoPreview,
|
||||||
startVideoPlayer,
|
startVideoPlayer,
|
||||||
@@ -451,6 +451,8 @@ export default function RandomUnit({
|
|||||||
patnrId: randomData.patnrId,
|
patnrId: randomData.patnrId,
|
||||||
prdtId: randomData.prdtId,
|
prdtId: randomData.prdtId,
|
||||||
curationId: randomData.lnkCurationId,
|
curationId: randomData.lnkCurationId,
|
||||||
|
sourcePanel: panel_names.HOME_PANEL,
|
||||||
|
sourceMenu: SOURCE_MENUS.HOME_RANDOM_UNIT,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
@@ -473,6 +475,8 @@ export default function RandomUnit({
|
|||||||
curationId: randomData.lnkCurationId,
|
curationId: randomData.lnkCurationId,
|
||||||
prdtId: randomData.prdtId,
|
prdtId: randomData.prdtId,
|
||||||
type: 'theme',
|
type: 'theme',
|
||||||
|
sourcePanel: panel_names.HOME_PANEL,
|
||||||
|
sourceMenu: SOURCE_MENUS.HOME_RANDOM_UNIT,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -1,42 +1,22 @@
|
|||||||
import React, {
|
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
useCallback,
|
|
||||||
useEffect,
|
|
||||||
useMemo,
|
|
||||||
useRef,
|
|
||||||
useState,
|
|
||||||
} from 'react';
|
|
||||||
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import {
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
useDispatch,
|
|
||||||
useSelector,
|
|
||||||
} from 'react-redux';
|
|
||||||
|
|
||||||
import Spotlight from '@enact/spotlight';
|
import Spotlight from '@enact/spotlight';
|
||||||
import SpotlightContainerDecorator
|
import SpotlightContainerDecorator from '@enact/spotlight/SpotlightContainerDecorator';
|
||||||
from '@enact/spotlight/SpotlightContainerDecorator';
|
|
||||||
import Spottable from '@enact/spotlight/Spottable';
|
import Spottable from '@enact/spotlight/Spottable';
|
||||||
import { getContainerId } from '@enact/spotlight/src/container';
|
import { getContainerId } from '@enact/spotlight/src/container';
|
||||||
|
|
||||||
import btnPlay from '../../../../assets/images/btn/btn-play-thumb-nor.png';
|
import btnPlay from '../../../../assets/images/btn/btn-play-thumb-nor.png';
|
||||||
import defaultLogoImg
|
import defaultLogoImg from '../../../../assets/images/ic-tab-partners-default@3x.png';
|
||||||
from '../../../../assets/images/ic-tab-partners-default@3x.png';
|
import emptyHorImage from '../../../../assets/images/img-home-banner-empty-hor.png';
|
||||||
import emptyHorImage
|
import emptyVerImage from '../../../../assets/images/img-home-banner-empty-ver.png';
|
||||||
from '../../../../assets/images/img-home-banner-empty-hor.png';
|
import defaultImageItem from '../../../../assets/images/img-thumb-empty-product@3x.png';
|
||||||
import emptyVerImage
|
|
||||||
from '../../../../assets/images/img-home-banner-empty-ver.png';
|
|
||||||
import defaultImageItem
|
|
||||||
from '../../../../assets/images/img-thumb-empty-product@3x.png';
|
|
||||||
import liveShow from '../../../../assets/images/tag-liveshow.png';
|
import liveShow from '../../../../assets/images/tag-liveshow.png';
|
||||||
import {
|
import { setBannerIndex, updateHomeInfo } from '../../../actions/homeActions';
|
||||||
setBannerIndex,
|
import { sendLogTopContents, sendLogTotalRecommend } from '../../../actions/logActions';
|
||||||
updateHomeInfo,
|
import { pushPanel, SOURCE_MENUS } from '../../../actions/panelActions';
|
||||||
} from '../../../actions/homeActions';
|
|
||||||
import {
|
|
||||||
sendLogTopContents,
|
|
||||||
sendLogTotalRecommend,
|
|
||||||
} from '../../../actions/logActions';
|
|
||||||
import { pushPanel } from '../../../actions/panelActions';
|
|
||||||
import { startVideoPlayer, finishVideoPreview } from '../../../actions/playActions';
|
import { startVideoPlayer, finishVideoPreview } from '../../../actions/playActions';
|
||||||
import CustomImage from '../../../components/CustomImage/CustomImage';
|
import CustomImage from '../../../components/CustomImage/CustomImage';
|
||||||
import usePriceInfo from '../../../hooks/usePriceInfo';
|
import usePriceInfo from '../../../hooks/usePriceInfo';
|
||||||
@@ -47,31 +27,25 @@ import {
|
|||||||
LOG_TP_NO,
|
LOG_TP_NO,
|
||||||
panel_names,
|
panel_names,
|
||||||
} from '../../../utils/Config';
|
} from '../../../utils/Config';
|
||||||
import {
|
import { $L, formatGMTString } from '../../../utils/helperMethods';
|
||||||
$L,
|
|
||||||
formatGMTString,
|
|
||||||
} from '../../../utils/helperMethods';
|
|
||||||
import { TEMPLATE_CODE_CONF } from '../HomePanel';
|
import { TEMPLATE_CODE_CONF } from '../HomePanel';
|
||||||
import css from './RollingUnit.module.less';
|
import css from './RollingUnit.module.less';
|
||||||
|
|
||||||
const SpottableComponent = Spottable("div");
|
const SpottableComponent = Spottable('div');
|
||||||
|
|
||||||
const Container = SpotlightContainerDecorator(
|
const Container = SpotlightContainerDecorator({ enterTo: 'last-focused', preserveId: true }, 'div');
|
||||||
{ enterTo: "last-focused", preserveId: true },
|
|
||||||
"div"
|
|
||||||
);
|
|
||||||
|
|
||||||
const LINK_TYPES = {
|
const LINK_TYPES = {
|
||||||
FEATURED_BRANDS: "DSP00501",
|
FEATURED_BRANDS: 'DSP00501',
|
||||||
TRENDING_NOW: "DSP00502",
|
TRENDING_NOW: 'DSP00502',
|
||||||
HOT_PICKS: "DSP00503",
|
HOT_PICKS: 'DSP00503',
|
||||||
ON_SALE: "DSP00504",
|
ON_SALE: 'DSP00504',
|
||||||
CATEGORY: "DSP00505",
|
CATEGORY: 'DSP00505',
|
||||||
PRODUCT_DETAIL: "DSP00506",
|
PRODUCT_DETAIL: 'DSP00506',
|
||||||
VOD: "DSP00507",
|
VOD: 'DSP00507',
|
||||||
SHOW_DETAIL: "DSP00508",
|
SHOW_DETAIL: 'DSP00508',
|
||||||
THEME: "DSP00509",
|
THEME: 'DSP00509',
|
||||||
JUSTFORYOU: "DSP00510",
|
JUSTFORYOU: 'DSP00510',
|
||||||
};
|
};
|
||||||
|
|
||||||
const createPanelInfo = (data, categoryData = {}) => ({
|
const createPanelInfo = (data, categoryData = {}) => ({
|
||||||
@@ -101,17 +75,11 @@ export default function RollingUnit({
|
|||||||
|
|
||||||
const { curationId, curationTitle } = useSelector((state) => state.home);
|
const { curationId, curationTitle } = useSelector((state) => state.home);
|
||||||
const curtNm = useSelector((state) => state.home?.bannerData?.curtNm);
|
const curtNm = useSelector((state) => state.home?.bannerData?.curtNm);
|
||||||
const shptmTmplCd = useSelector(
|
const shptmTmplCd = useSelector((state) => state.home?.bannerData?.shptmTmplCd);
|
||||||
(state) => state.home?.bannerData?.shptmTmplCd
|
|
||||||
);
|
|
||||||
const nowMenu = useSelector((state) => state.common.menu.nowMenu);
|
const nowMenu = useSelector((state) => state.common.menu.nowMenu);
|
||||||
const entryMenu = useSelector((state) => state.common.menu.entryMenu);
|
const entryMenu = useSelector((state) => state.common.menu.entryMenu);
|
||||||
const introTermsAgree = useSelector(
|
const introTermsAgree = useSelector((state) => state.common.optionalTermsAgree);
|
||||||
(state) => state.common.optionalTermsAgree
|
const homeCategory = useSelector((state) => state.home.menuData?.data?.homeCategory);
|
||||||
);
|
|
||||||
const homeCategory = useSelector(
|
|
||||||
(state) => state.home.menuData?.data?.homeCategory
|
|
||||||
);
|
|
||||||
const countryCode = useSelector((state) => state.common.httpHeader.cntry_cd);
|
const countryCode = useSelector((state) => state.common.httpHeader.cntry_cd);
|
||||||
|
|
||||||
const { userNumber } = useSelector((state) => state.common.appStatus.loginUserData);
|
const { userNumber } = useSelector((state) => state.common.appStatus.loginUserData);
|
||||||
@@ -127,15 +95,15 @@ export default function RollingUnit({
|
|||||||
// 컴포넌트 상단에서 필터링
|
// 컴포넌트 상단에서 필터링
|
||||||
const filteredRollingData = useMemo(() => {
|
const filteredRollingData = useMemo(() => {
|
||||||
return rollingData.filter(
|
return rollingData.filter(
|
||||||
(item) => (introTermsAgree === true && (userNumber !== undefined && userNumber !== '')) || item.shptmLnkTpCd !== "DSP00510"
|
(item) =>
|
||||||
|
(introTermsAgree === true && userNumber !== undefined && userNumber !== '') ||
|
||||||
|
item.shptmLnkTpCd !== 'DSP00510'
|
||||||
);
|
);
|
||||||
}, [rollingData, introTermsAgree, userNumber]);
|
}, [rollingData, introTermsAgree, userNumber]);
|
||||||
|
|
||||||
// 이후 filteredRollingData 사용
|
// 이후 filteredRollingData 사용
|
||||||
const rollingDataLength = filteredRollingData.length;
|
const rollingDataLength = filteredRollingData.length;
|
||||||
const [startIndex, setStartIndex] = useState(
|
const [startIndex, setStartIndex] = useState(savedIndex !== undefined ? savedIndex : 0);
|
||||||
savedIndex !== undefined ? savedIndex : 0
|
|
||||||
);
|
|
||||||
const lastIndexRef = useRef(rollingDataLength - 1);
|
const lastIndexRef = useRef(rollingDataLength - 1);
|
||||||
const doRollingRef = useRef(false);
|
const doRollingRef = useRef(false);
|
||||||
const [unitHasFocus, setUnitHasFocus] = useState(false);
|
const [unitHasFocus, setUnitHasFocus] = useState(false);
|
||||||
@@ -157,14 +125,14 @@ export default function RollingUnit({
|
|||||||
|
|
||||||
switch (currentRollingData?.shptmBanrTpCd) {
|
switch (currentRollingData?.shptmBanrTpCd) {
|
||||||
// case: "LIVE" or "VOD"
|
// case: "LIVE" or "VOD"
|
||||||
case "DSP00301":
|
case 'DSP00301':
|
||||||
case "DSP00302":
|
case 'DSP00302':
|
||||||
contId = currentRollingData?.showId;
|
contId = currentRollingData?.showId;
|
||||||
contNm = currentRollingData?.showNm;
|
contNm = currentRollingData?.showNm;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// case: "Image Banner"
|
// case: "Image Banner"
|
||||||
case "DSP00303":
|
case 'DSP00303':
|
||||||
contId = currentRollingData?.shptmLnkTpCd;
|
contId = currentRollingData?.shptmLnkTpCd;
|
||||||
contNm = currentRollingData?.shptmLnkTpNm;
|
contNm = currentRollingData?.shptmLnkTpNm;
|
||||||
break;
|
break;
|
||||||
@@ -177,27 +145,27 @@ export default function RollingUnit({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
currentRollingData?.shptmLnkTpCd === "DSP00503" || // "Hot Picks"
|
currentRollingData?.shptmLnkTpCd === 'DSP00503' || // "Hot Picks"
|
||||||
currentRollingData?.shptmLnkTpCd === "DSP00509" // "Theme"
|
currentRollingData?.shptmLnkTpCd === 'DSP00509' // "Theme"
|
||||||
) {
|
) {
|
||||||
contNm = contNm + " | " + currentRollingData?.lnkCurationId;
|
contNm = contNm + ' | ' + currentRollingData?.lnkCurationId;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
banrNo: `${currentRollingData?.banrDpOrd}`,
|
banrNo: `${currentRollingData?.banrDpOrd}`,
|
||||||
banrTpNm: currentRollingData?.vtctpYn
|
banrTpNm: currentRollingData?.vtctpYn
|
||||||
? currentRollingData.vtctpYn === "Y"
|
? currentRollingData.vtctpYn === 'Y'
|
||||||
? "Vertical"
|
? 'Vertical'
|
||||||
: "Horizontal"
|
: 'Horizontal'
|
||||||
: "",
|
: '',
|
||||||
contId,
|
contId,
|
||||||
contNm,
|
contNm,
|
||||||
contTpNm: currentRollingData?.shptmBanrTpNm ?? "",
|
contTpNm: currentRollingData?.shptmBanrTpNm ?? '',
|
||||||
dspyTpNm: bannerDataRef.current?.shptmDspyTpNm ?? "",
|
dspyTpNm: bannerDataRef.current?.shptmDspyTpNm ?? '',
|
||||||
expsOrd: bannerDataRef.current?.banrLctnNo ?? "",
|
expsOrd: bannerDataRef.current?.banrLctnNo ?? '',
|
||||||
linkTpCd: "",
|
linkTpCd: '',
|
||||||
patncNm: currentRollingData?.patncNm ?? "",
|
patncNm: currentRollingData?.patncNm ?? '',
|
||||||
patnrId: currentRollingData?.patnrId ?? "",
|
patnrId: currentRollingData?.patnrId ?? '',
|
||||||
tmplCd: shptmTmplCd,
|
tmplCd: shptmTmplCd,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -208,19 +176,17 @@ export default function RollingUnit({
|
|||||||
(bannerClick) => {
|
(bannerClick) => {
|
||||||
const data = rollingDataRef.current[startIndex];
|
const data = rollingDataRef.current[startIndex];
|
||||||
const newParams =
|
const newParams =
|
||||||
bannerData.banrLctnNo === "2"
|
bannerData.banrLctnNo === '2'
|
||||||
? {
|
? {
|
||||||
bannerType: "Horizontal",
|
bannerType: 'Horizontal',
|
||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
bannerType: "Vertical",
|
bannerType: 'Vertical',
|
||||||
};
|
};
|
||||||
if (rollingDataRef.current && nowMenu === LOG_MENU.HOME_TOP) {
|
if (rollingDataRef.current && nowMenu === LOG_MENU.HOME_TOP) {
|
||||||
const logParams = {
|
const logParams = {
|
||||||
contextName: LOG_CONTEXT_NAME.HOME,
|
contextName: LOG_CONTEXT_NAME.HOME,
|
||||||
messageId: bannerClick
|
messageId: bannerClick ? LOG_MESSAGE_ID.BANNER_CLICK : LOG_MESSAGE_ID.BANNER,
|
||||||
? LOG_MESSAGE_ID.BANNER_CLICK
|
|
||||||
: LOG_MESSAGE_ID.BANNER,
|
|
||||||
curationId: curationId,
|
curationId: curationId,
|
||||||
curationTitle: curationTitle,
|
curationTitle: curationTitle,
|
||||||
contentType: data.shptmBanrTpNm,
|
contentType: data.shptmBanrTpNm,
|
||||||
@@ -228,7 +194,7 @@ export default function RollingUnit({
|
|||||||
contentTitle: data.showNm,
|
contentTitle: data.showNm,
|
||||||
productId: data.prdtId,
|
productId: data.prdtId,
|
||||||
productTitle: data.prdtNm,
|
productTitle: data.prdtNm,
|
||||||
displayType: "button",
|
displayType: 'button',
|
||||||
partner: data.patncNm,
|
partner: data.patncNm,
|
||||||
brand: data.brndNm,
|
brand: data.brndNm,
|
||||||
location: bannerData.banrLctnNo,
|
location: bannerData.banrLctnNo,
|
||||||
@@ -251,16 +217,14 @@ export default function RollingUnit({
|
|||||||
const deltaTime = time - previousTimeRef.current;
|
const deltaTime = time - previousTimeRef.current;
|
||||||
|
|
||||||
if (deltaTime >= 10000 && doRollingRef.current) {
|
if (deltaTime >= 10000 && doRollingRef.current) {
|
||||||
setStartIndex((prevIndex) =>
|
setStartIndex((prevIndex) => (prevIndex === lastIndexRef.current ? 0 : prevIndex + 1));
|
||||||
prevIndex === lastIndexRef.current ? 0 : prevIndex + 1
|
|
||||||
);
|
|
||||||
previousTimeRef.current = time;
|
previousTimeRef.current = time;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
previousTimeRef.current = time;
|
previousTimeRef.current = time;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof window === "object") {
|
if (typeof window === 'object') {
|
||||||
requestRef.current = window.requestAnimationFrame(animate);
|
requestRef.current = window.requestAnimationFrame(animate);
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
@@ -298,7 +262,7 @@ export default function RollingUnit({
|
|||||||
|
|
||||||
// 인디케이터 아래키 누를시 [<]
|
// 인디케이터 아래키 누를시 [<]
|
||||||
const prevKeyDown = (event) => {
|
const prevKeyDown = (event) => {
|
||||||
if (event.key === "ArrowDown") {
|
if (event.key === 'ArrowDown') {
|
||||||
setNextFocus(true);
|
setNextFocus(true);
|
||||||
setContentsFocus(true);
|
setContentsFocus(true);
|
||||||
}
|
}
|
||||||
@@ -306,7 +270,7 @@ export default function RollingUnit({
|
|||||||
|
|
||||||
// 인디케이터 아래키 누를시 [>]
|
// 인디케이터 아래키 누를시 [>]
|
||||||
const nextKeyDown = (event) => {
|
const nextKeyDown = (event) => {
|
||||||
if (event.key === "ArrowDown") {
|
if (event.key === 'ArrowDown') {
|
||||||
setPrevFocus(true);
|
setPrevFocus(true);
|
||||||
setContentsFocus(true);
|
setContentsFocus(true);
|
||||||
}
|
}
|
||||||
@@ -364,7 +328,16 @@ export default function RollingUnit({
|
|||||||
|
|
||||||
const handlePushPanel = useCallback(
|
const handlePushPanel = useCallback(
|
||||||
(name, panelInfo) => {
|
(name, panelInfo) => {
|
||||||
dispatch(pushPanel({ name, panelInfo }));
|
const isDetailPanel = name === panel_names.DETAIL_PANEL;
|
||||||
|
const enrichedPanelInfo = isDetailPanel
|
||||||
|
? {
|
||||||
|
sourcePanel: panel_names.HOME_PANEL,
|
||||||
|
sourceMenu: SOURCE_MENUS.HOME_ROLLING_UNIT,
|
||||||
|
...panelInfo,
|
||||||
|
}
|
||||||
|
: panelInfo;
|
||||||
|
|
||||||
|
dispatch(pushPanel({ name, panelInfo: enrichedPanelInfo }));
|
||||||
},
|
},
|
||||||
[dispatch]
|
[dispatch]
|
||||||
);
|
);
|
||||||
@@ -390,7 +363,7 @@ export default function RollingUnit({
|
|||||||
dispatch(
|
dispatch(
|
||||||
sendLogTopContents({
|
sendLogTopContents({
|
||||||
...topContentsLogInfo,
|
...topContentsLogInfo,
|
||||||
inDt: formatGMTString(new Date()) ?? "",
|
inDt: formatGMTString(new Date()) ?? '',
|
||||||
logTpNo: LOG_TP_NO.TOP_CONTENTS.CLICK,
|
logTpNo: LOG_TP_NO.TOP_CONTENTS.CLICK,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@@ -400,26 +373,20 @@ export default function RollingUnit({
|
|||||||
switch (linkType) {
|
switch (linkType) {
|
||||||
case LINK_TYPES.FEATURED_BRANDS:
|
case LINK_TYPES.FEATURED_BRANDS:
|
||||||
handlePushPanel(panel_names.FEATURED_BRANDS_PANEL, {
|
handlePushPanel(panel_names.FEATURED_BRANDS_PANEL, {
|
||||||
from: "gnb",
|
from: 'gnb',
|
||||||
patnrId: currentData.patnrId,
|
patnrId: currentData.patnrId,
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case LINK_TYPES.TRENDING_NOW:
|
case LINK_TYPES.TRENDING_NOW:
|
||||||
handlePushPanel(
|
handlePushPanel(panel_names.TRENDING_NOW_PANEL, createPanelInfo(currentData));
|
||||||
panel_names.TRENDING_NOW_PANEL,
|
|
||||||
createPanelInfo(currentData)
|
|
||||||
);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case LINK_TYPES.HOT_PICKS:
|
case LINK_TYPES.HOT_PICKS:
|
||||||
if (playerPanelInfo?.modal) {
|
if (playerPanelInfo?.modal) {
|
||||||
dispatch(finishVideoPreview());
|
dispatch(finishVideoPreview());
|
||||||
}
|
}
|
||||||
handlePushPanel(
|
handlePushPanel(panel_names.HOT_PICKS_PANEL, createPanelInfo(currentData));
|
||||||
panel_names.HOT_PICKS_PANEL,
|
|
||||||
createPanelInfo(currentData)
|
|
||||||
);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case LINK_TYPES.ON_SALE:
|
case LINK_TYPES.ON_SALE:
|
||||||
@@ -430,10 +397,7 @@ export default function RollingUnit({
|
|||||||
|
|
||||||
case LINK_TYPES.CATEGORY:
|
case LINK_TYPES.CATEGORY:
|
||||||
if (Object.keys(categoryData).length > 0) {
|
if (Object.keys(categoryData).length > 0) {
|
||||||
handlePushPanel(
|
handlePushPanel(panel_names.CATEGORY_PANEL, createPanelInfo(currentData, categoryData));
|
||||||
panel_names.CATEGORY_PANEL,
|
|
||||||
createPanelInfo(currentData, categoryData)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -454,10 +418,7 @@ export default function RollingUnit({
|
|||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case LINK_TYPES.JUSTFORYOU:
|
case LINK_TYPES.JUSTFORYOU:
|
||||||
handlePushPanel(
|
handlePushPanel(panel_names.JUST_FOR_YOU_TEST_PANEL, createPanelInfo(currentData));
|
||||||
panel_names.JUST_FOR_YOU_TEST_PANEL,
|
|
||||||
createPanelInfo(currentData)
|
|
||||||
);
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return;
|
return;
|
||||||
@@ -466,7 +427,7 @@ export default function RollingUnit({
|
|||||||
dispatch(
|
dispatch(
|
||||||
sendLogTopContents({
|
sendLogTopContents({
|
||||||
...topContentsLogInfo,
|
...topContentsLogInfo,
|
||||||
inDt: formatGMTString(new Date()) ?? "",
|
inDt: formatGMTString(new Date()) ?? '',
|
||||||
logTpNo: LOG_TP_NO.TOP_CONTENTS.CLICK,
|
logTpNo: LOG_TP_NO.TOP_CONTENTS.CLICK,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@@ -492,7 +453,7 @@ export default function RollingUnit({
|
|||||||
panelInfo: {
|
panelInfo: {
|
||||||
lastFocusedTargetId,
|
lastFocusedTargetId,
|
||||||
focusedContainerId: TEMPLATE_CODE_CONF.TOP,
|
focusedContainerId: TEMPLATE_CODE_CONF.TOP,
|
||||||
currentSpot: currentSpot?.getAttribute("data-spotlight-id"),
|
currentSpot: currentSpot?.getAttribute('data-spotlight-id'),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@@ -508,7 +469,7 @@ export default function RollingUnit({
|
|||||||
showUrl: currentData.showUrl,
|
showUrl: currentData.showUrl,
|
||||||
patnrId: currentData.patnrId,
|
patnrId: currentData.patnrId,
|
||||||
showId: currentData.showId,
|
showId: currentData.showId,
|
||||||
shptmBanrTpNm: currentData.showId ? currentData.shptmBanrTpNm : "MEDIA",
|
shptmBanrTpNm: currentData.showId ? currentData.shptmBanrTpNm : 'MEDIA',
|
||||||
lgCatCd: currentData.lgCatCd,
|
lgCatCd: currentData.lgCatCd,
|
||||||
chanId: currentData.brdcChnlId,
|
chanId: currentData.brdcChnlId,
|
||||||
modal: false,
|
modal: false,
|
||||||
@@ -520,18 +481,11 @@ export default function RollingUnit({
|
|||||||
dispatch(
|
dispatch(
|
||||||
sendLogTopContents({
|
sendLogTopContents({
|
||||||
...topContentsLogInfo,
|
...topContentsLogInfo,
|
||||||
inDt: formatGMTString(new Date()) ?? "",
|
inDt: formatGMTString(new Date()) ?? '',
|
||||||
logTpNo: LOG_TP_NO.TOP_CONTENTS.CLICK,
|
logTpNo: LOG_TP_NO.TOP_CONTENTS.CLICK,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}, [
|
}, [rollingData, startIndex, bannerId, dispatch, handleStartVideoPlayer, topContentsLogInfo]);
|
||||||
rollingData,
|
|
||||||
startIndex,
|
|
||||||
bannerId,
|
|
||||||
dispatch,
|
|
||||||
handleStartVideoPlayer,
|
|
||||||
topContentsLogInfo,
|
|
||||||
]);
|
|
||||||
|
|
||||||
// 10초 롤링
|
// 10초 롤링
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -540,20 +494,20 @@ export default function RollingUnit({
|
|||||||
previousTimeRef.current = undefined;
|
previousTimeRef.current = undefined;
|
||||||
if (rollingDataLength <= 1 || unitHasFocus) {
|
if (rollingDataLength <= 1 || unitHasFocus) {
|
||||||
doRollingRef.current = false;
|
doRollingRef.current = false;
|
||||||
if (typeof window === "object") {
|
if (typeof window === 'object') {
|
||||||
window.cancelAnimationFrame(requestRef.current);
|
window.cancelAnimationFrame(requestRef.current);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
doRollingRef.current = true;
|
doRollingRef.current = true;
|
||||||
if (typeof window === "object") {
|
if (typeof window === 'object') {
|
||||||
requestRef.current = window.requestAnimationFrame(animate);
|
requestRef.current = window.requestAnimationFrame(animate);
|
||||||
}
|
}
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
doRollingRef.current = false;
|
doRollingRef.current = false;
|
||||||
if (typeof window === "object") {
|
if (typeof window === 'object') {
|
||||||
window.cancelAnimationFrame(requestRef.current);
|
window.cancelAnimationFrame(requestRef.current);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -567,7 +521,7 @@ export default function RollingUnit({
|
|||||||
const params = {
|
const params = {
|
||||||
...topContentsLogInfo,
|
...topContentsLogInfo,
|
||||||
entryMenu: _entryMenu,
|
entryMenu: _entryMenu,
|
||||||
inDt: formatGMTString(new Date()) ?? "",
|
inDt: formatGMTString(new Date()) ?? '',
|
||||||
logTpNo: LOG_TP_NO.TOP_CONTENTS.VIEW,
|
logTpNo: LOG_TP_NO.TOP_CONTENTS.VIEW,
|
||||||
nowMenu: _nowMenu,
|
nowMenu: _nowMenu,
|
||||||
};
|
};
|
||||||
@@ -593,10 +547,7 @@ export default function RollingUnit({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Container
|
<Container
|
||||||
className={classNames(
|
className={classNames(css.rollingWrap, isHorizontal && css.isHorizontalWrap)}
|
||||||
css.rollingWrap,
|
|
||||||
isHorizontal && css.isHorizontalWrap
|
|
||||||
)}
|
|
||||||
spotlightId={`container-${spotlightId}`}
|
spotlightId={`container-${spotlightId}`}
|
||||||
onFocus={shelfFocus}
|
onFocus={shelfFocus}
|
||||||
>
|
>
|
||||||
@@ -606,15 +557,14 @@ export default function RollingUnit({
|
|||||||
onClick={handlePrev}
|
onClick={handlePrev}
|
||||||
onFocus={indicatorFocus}
|
onFocus={indicatorFocus}
|
||||||
onBlur={indicatorBlur}
|
onBlur={indicatorBlur}
|
||||||
spotlightId={spotlightId + "Prev"}
|
spotlightId={spotlightId + 'Prev'}
|
||||||
spotlightDisabled={prevFocus}
|
spotlightDisabled={prevFocus}
|
||||||
onKeyDown={prevKeyDown}
|
onKeyDown={prevKeyDown}
|
||||||
aria-label="Move to left Button"
|
aria-label="Move to left Button"
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
{filteredRollingData &&
|
{filteredRollingData && filteredRollingData[startIndex].shptmBanrTpNm === 'Image Banner' ? (
|
||||||
filteredRollingData[startIndex].shptmBanrTpNm === "Image Banner" ? (
|
|
||||||
<SpottableComponent
|
<SpottableComponent
|
||||||
className={classNames(css.itemBox, isHorizontal && css.isHorizontal)}
|
className={classNames(css.itemBox, isHorizontal && css.isHorizontal)}
|
||||||
onClick={imageBannerClick}
|
onClick={imageBannerClick}
|
||||||
@@ -632,7 +582,7 @@ export default function RollingUnit({
|
|||||||
<img src={filteredRollingData[startIndex].tmnlImgPath} />
|
<img src={filteredRollingData[startIndex].tmnlImgPath} />
|
||||||
</div>
|
</div>
|
||||||
</SpottableComponent>
|
</SpottableComponent>
|
||||||
) : filteredRollingData[startIndex].shptmBanrTpNm === "LIVE" ? (
|
) : filteredRollingData[startIndex].shptmBanrTpNm === 'LIVE' ? (
|
||||||
<SpottableComponent
|
<SpottableComponent
|
||||||
className={classNames(css.itemBox, isHorizontal && css.isHorizontal)}
|
className={classNames(css.itemBox, isHorizontal && css.isHorizontal)}
|
||||||
onClick={videoClick}
|
onClick={videoClick}
|
||||||
@@ -640,30 +590,18 @@ export default function RollingUnit({
|
|||||||
onBlur={onBlur}
|
onBlur={onBlur}
|
||||||
spotlightId={spotlightId}
|
spotlightId={spotlightId}
|
||||||
spotlightDisabled={contentsFocus}
|
spotlightDisabled={contentsFocus}
|
||||||
aria-label={"LIVE " + filteredRollingData[startIndex].showNm}
|
aria-label={'LIVE ' + filteredRollingData[startIndex].showNm}
|
||||||
>
|
>
|
||||||
<p className={css.liveIcon}>
|
<p className={css.liveIcon}>
|
||||||
<CustomImage
|
<CustomImage delay={0} src={liveShow} animationSpeed="fast" ariaLabel="LIVE icon" />
|
||||||
delay={0}
|
|
||||||
src={liveShow}
|
|
||||||
animationSpeed="fast"
|
|
||||||
ariaLabel="LIVE icon"
|
|
||||||
/>
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div
|
<div className={classNames(css.itemBox, isHorizontal && css.isHorizontal)}>
|
||||||
className={classNames(
|
|
||||||
css.itemBox,
|
|
||||||
isHorizontal && css.isHorizontal
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{filteredRollingData[startIndex].tmnlImgPath == null ? (
|
{filteredRollingData[startIndex].tmnlImgPath == null ? (
|
||||||
<CustomImage
|
<CustomImage
|
||||||
delay={0}
|
delay={0}
|
||||||
src={
|
src={
|
||||||
filteredRollingData[startIndex].vtctpYn === "Y"
|
filteredRollingData[startIndex].vtctpYn === 'Y' ? emptyVerImage : emptyHorImage
|
||||||
? emptyVerImage
|
|
||||||
: emptyHorImage
|
|
||||||
}
|
}
|
||||||
ariaLabel={filteredRollingData[startIndex].tmnlImgNm}
|
ariaLabel={filteredRollingData[startIndex].tmnlImgNm}
|
||||||
fallbackSrc={isHorizontal ? emptyHorImage : emptyVerImage}
|
fallbackSrc={isHorizontal ? emptyHorImage : emptyVerImage}
|
||||||
@@ -679,13 +617,9 @@ export default function RollingUnit({
|
|||||||
|
|
||||||
<div className={css.btnPlay}>
|
<div className={css.btnPlay}>
|
||||||
{filteredRollingData[startIndex].tmnlImgPath == null ? (
|
{filteredRollingData[startIndex].tmnlImgPath == null ? (
|
||||||
""
|
''
|
||||||
) : (
|
) : (
|
||||||
<CustomImage
|
<CustomImage delay={0} src={btnPlay} ariaLabel="Play video Button" />
|
||||||
delay={0}
|
|
||||||
src={btnPlay}
|
|
||||||
ariaLabel="Play video Button"
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -700,7 +634,7 @@ export default function RollingUnit({
|
|||||||
/>
|
/>
|
||||||
</p>
|
</p>
|
||||||
</SpottableComponent>
|
</SpottableComponent>
|
||||||
) : filteredRollingData[startIndex].shptmBanrTpNm === "VOD" ? (
|
) : filteredRollingData[startIndex].shptmBanrTpNm === 'VOD' ? (
|
||||||
<SpottableComponent
|
<SpottableComponent
|
||||||
className={classNames(css.itemBox, isHorizontal && css.isHorizontal)}
|
className={classNames(css.itemBox, isHorizontal && css.isHorizontal)}
|
||||||
onClick={videoClick}
|
onClick={videoClick}
|
||||||
@@ -710,19 +644,12 @@ export default function RollingUnit({
|
|||||||
spotlightDisabled={contentsFocus}
|
spotlightDisabled={contentsFocus}
|
||||||
aria-label={filteredRollingData[startIndex].showNm}
|
aria-label={filteredRollingData[startIndex].showNm}
|
||||||
>
|
>
|
||||||
<div
|
<div className={classNames(css.itemBox, isHorizontal && css.isHorizontal)}>
|
||||||
className={classNames(
|
|
||||||
css.itemBox,
|
|
||||||
isHorizontal && css.isHorizontal
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{filteredRollingData[startIndex].tmnlImgPath == null ? (
|
{filteredRollingData[startIndex].tmnlImgPath == null ? (
|
||||||
<CustomImage
|
<CustomImage
|
||||||
delay={0}
|
delay={0}
|
||||||
src={
|
src={
|
||||||
filteredRollingData[startIndex].vtctpYn === "Y"
|
filteredRollingData[startIndex].vtctpYn === 'Y' ? emptyVerImage : emptyHorImage
|
||||||
? emptyVerImage
|
|
||||||
: emptyHorImage
|
|
||||||
}
|
}
|
||||||
ariaLabel={filteredRollingData[startIndex].tmnlImgNm}
|
ariaLabel={filteredRollingData[startIndex].tmnlImgNm}
|
||||||
/>
|
/>
|
||||||
@@ -736,13 +663,9 @@ export default function RollingUnit({
|
|||||||
|
|
||||||
<div className={css.btnPlay}>
|
<div className={css.btnPlay}>
|
||||||
{filteredRollingData[startIndex].tmnlImgPath == null ? (
|
{filteredRollingData[startIndex].tmnlImgPath == null ? (
|
||||||
""
|
''
|
||||||
) : (
|
) : (
|
||||||
<CustomImage
|
<CustomImage delay={0} src={btnPlay} ariaLabel="Play video Button" />
|
||||||
delay={0}
|
|
||||||
src={btnPlay}
|
|
||||||
ariaLabel="Play video Button"
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -764,8 +687,8 @@ export default function RollingUnit({
|
|||||||
className={classNames(
|
className={classNames(
|
||||||
css.itemBox,
|
css.itemBox,
|
||||||
css.todaysDeals,
|
css.todaysDeals,
|
||||||
countryCode === "RU" ? css.ru : "",
|
countryCode === 'RU' ? css.ru : '',
|
||||||
countryCode === "DE" ? css.de : "",
|
countryCode === 'DE' ? css.de : '',
|
||||||
isHorizontal && css.isHorizontal
|
isHorizontal && css.isHorizontal
|
||||||
)}
|
)}
|
||||||
onClick={imageBannerClick}
|
onClick={imageBannerClick}
|
||||||
@@ -788,7 +711,7 @@ export default function RollingUnit({
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<div className={css.accBox}>
|
<div className={css.accBox}>
|
||||||
{parseFloat(originalPrice?.replace("$", "")) === 0
|
{parseFloat(originalPrice?.replace('$', '')) === 0
|
||||||
? filteredRollingData[startIndex].offerInfo
|
? filteredRollingData[startIndex].offerInfo
|
||||||
: discountRate
|
: discountRate
|
||||||
? discountedPrice
|
? discountedPrice
|
||||||
@@ -797,10 +720,9 @@ export default function RollingUnit({
|
|||||||
<span className={css.saleAccBox}>{originalPrice}</span>
|
<span className={css.saleAccBox}>{originalPrice}</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{isHorizontal &&
|
{isHorizontal && parseFloat(originalPrice?.replace('$', '')) !== 0 && (
|
||||||
parseFloat(originalPrice?.replace("$", "")) !== 0 && (
|
<span className={css.saleAccBox}>{originalPrice}</span>
|
||||||
<span className={css.saleAccBox}>{originalPrice}</span>
|
)}
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={css.itemImgBox}>
|
<div className={css.itemImgBox}>
|
||||||
@@ -822,7 +744,7 @@ export default function RollingUnit({
|
|||||||
onClick={handleNext}
|
onClick={handleNext}
|
||||||
onFocus={indicatorFocus}
|
onFocus={indicatorFocus}
|
||||||
onBlur={indicatorBlur}
|
onBlur={indicatorBlur}
|
||||||
spotlightId={spotlightId + "Next"}
|
spotlightId={spotlightId + 'Next'}
|
||||||
spotlightDisabled={nextFocus}
|
spotlightDisabled={nextFocus}
|
||||||
onKeyDown={nextKeyDown}
|
onKeyDown={nextKeyDown}
|
||||||
aria-label="Move to right Button"
|
aria-label="Move to right Button"
|
||||||
|
|||||||
@@ -244,6 +244,31 @@ const HomePanel = ({ isOnTop, showGradientBackground = false }) => {
|
|||||||
const focusedContainerIdRef = useRef(null);
|
const focusedContainerIdRef = useRef(null);
|
||||||
const prevIsOnTopRef = useRef(isOnTop);
|
const prevIsOnTopRef = useRef(isOnTop);
|
||||||
|
|
||||||
|
// ✅ [251124] HomePanel을 벗어날 때(isOnTop: true -> false) 현재 포커스 저장
|
||||||
|
useEffect(() => {
|
||||||
|
if (prevIsOnTopRef.current && !isOnTop) {
|
||||||
|
const current = Spotlight.getCurrent();
|
||||||
|
const tBody = document.querySelector(`[data-spotlight-id="${SpotlightIds.HOME_TBODY}"]`);
|
||||||
|
|
||||||
|
if (current && tBody && tBody.contains(current)) {
|
||||||
|
const targetId = current.getAttribute('data-spotlight-id');
|
||||||
|
if (targetId) {
|
||||||
|
dlog('[HomePanel] Saving focus before leaving:', targetId);
|
||||||
|
lastFocusedTargetRef.current = targetId;
|
||||||
|
dispatch(
|
||||||
|
updateHomeInfo({
|
||||||
|
name: panel_names.HOME_PANEL,
|
||||||
|
panelInfo: {
|
||||||
|
lastFocusedTargetId: targetId,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
prevIsOnTopRef.current = isOnTop;
|
||||||
|
}, [isOnTop, dispatch]);
|
||||||
|
|
||||||
// ✅ [251119] DetailPanelBackground 이미지 프리로딩
|
// ✅ [251119] DetailPanelBackground 이미지 프리로딩
|
||||||
// HomePanel 마운트 시 백그라운드로 모든 파트너사 배경 이미지를 미리 로드하여
|
// HomePanel 마운트 시 백그라운드로 모든 파트너사 배경 이미지를 미리 로드하여
|
||||||
// DetailPanel 진입 시 로딩 지연을 방지함
|
// DetailPanel 진입 시 로딩 지연을 방지함
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ import {
|
|||||||
pauseModalVideo,
|
pauseModalVideo,
|
||||||
resumeModalVideo,
|
resumeModalVideo,
|
||||||
resumeFullscreenVideo,
|
resumeFullscreenVideo,
|
||||||
|
updateVideoPlayState,
|
||||||
} from '../../actions/playActions';
|
} from '../../actions/playActions';
|
||||||
import { resetPlayerOverlays } from '../../actions/videoPlayActions';
|
import { resetPlayerOverlays } from '../../actions/videoPlayActions';
|
||||||
import { convertUtcToLocal } from '../../components/MediaPlayer/util';
|
import { convertUtcToLocal } from '../../components/MediaPlayer/util';
|
||||||
@@ -436,17 +437,153 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props
|
|||||||
}
|
}
|
||||||
}, [isOnTop, panelInfo]);
|
}, [isOnTop, panelInfo]);
|
||||||
|
|
||||||
// 새로운 useEffect 추가 (라인 328 이후)
|
// 이전 상태 저장을 위한 ref
|
||||||
|
const previousVideoPlayState = useRef(null);
|
||||||
|
const previousPanelInfo = useRef(null);
|
||||||
|
const previousVideoSource = useRef(null);
|
||||||
|
const previousIsOnTop = useRef(null);
|
||||||
|
|
||||||
|
// Redux 상태 변화 모니터링 useEffect (중요 변화만)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// modal 여부와 관계없이 videoPlayer가 있고 isPaused 값이 명시적일 때 제어
|
// HomePanel이 최상위이고 videoPlayState가 실제로 변경되었을 때만 로그
|
||||||
if (videoPlayer.current && panelInfo?.isPaused !== undefined) {
|
const isOnTop = panel_names.HOME_PANEL === topPanel?.name;
|
||||||
if (panelInfo.isPaused === true) {
|
const hasSignificantChange =
|
||||||
videoPlayer.current.pause();
|
previousVideoPlayState.current?.isPlaying !== videoPlayState?.isPlaying ||
|
||||||
} else if (panelInfo.isPaused === false) {
|
previousVideoPlayState.current?.isPaused !== videoPlayState?.isPaused ||
|
||||||
videoPlayer.current.play();
|
Math.abs(
|
||||||
|
(previousVideoPlayState.current?.currentTime || 0) - (videoPlayState?.currentTime || 0)
|
||||||
|
) > 1;
|
||||||
|
|
||||||
|
// 🔍 DetailPanel으로 인한 일시정지 상태 변화도 모니터링
|
||||||
|
const isDetailPanelOnTop = panel_names.DETAIL_PANEL === topPanel?.name;
|
||||||
|
const isPlayingChanged =
|
||||||
|
previousVideoPlayState.current?.isPlaying !== videoPlayState?.isPlaying;
|
||||||
|
const isPausedChanged = previousVideoPlayState.current?.isPaused !== videoPlayState?.isPaused;
|
||||||
|
|
||||||
|
if (
|
||||||
|
(isOnTop && videoPlayState && hasSignificantChange) ||
|
||||||
|
(isDetailPanelOnTop && (isPlayingChanged || isPausedChanged))
|
||||||
|
) {
|
||||||
|
console.log('📊 [PlayerPanel] Significant videoPlayState change', {
|
||||||
|
previousState: previousVideoPlayState.current,
|
||||||
|
currentState: videoPlayState,
|
||||||
|
topPanelName: topPanel?.name,
|
||||||
|
isOnTop,
|
||||||
|
isDetailPanelOnTop,
|
||||||
|
videoPlayerExists: !!videoPlayer.current,
|
||||||
|
currentPlayingUrl,
|
||||||
|
changeReason: isDetailPanelOnTop ? 'DetailPanel transition' : 'HomePanel state change',
|
||||||
|
// 🔍 Redux paused 상태 특별 확인
|
||||||
|
reduxIsPaused: videoPlayState?.isPaused,
|
||||||
|
reduxIsPlaying: videoPlayState?.isPlaying,
|
||||||
|
panelInfoIsPaused: panelInfo?.isPaused,
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
source: 'useEffect videoPlayState',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
previousVideoPlayState.current = videoPlayState;
|
||||||
|
}, [videoPlayState, topPanel?.name, panelInfo?.isPaused]);
|
||||||
|
|
||||||
|
// PanelInfo 상태 변화 모니터링 useEffect (isPaused가 실제로 변경될 때만)
|
||||||
|
useEffect(() => {
|
||||||
|
const isOnTop = panel_names.HOME_PANEL === topPanel?.name;
|
||||||
|
const isPausedChanged = previousPanelInfo.current?.isPaused !== panelInfo?.isPaused;
|
||||||
|
|
||||||
|
if (isOnTop && panelInfo?.isPaused !== undefined && isPausedChanged) {
|
||||||
|
// 상태 변경 시에만 디버깅 로그 출력
|
||||||
|
console.log('🔍 [PlayerPanel] PanelInfo isPaused changed', {
|
||||||
|
previousIsPaused: previousPanelInfo.current?.isPaused,
|
||||||
|
currentIsPaused: panelInfo.isPaused,
|
||||||
|
isOnTop,
|
||||||
|
videoPlayerExists: !!videoPlayer.current,
|
||||||
|
currentPlayingUrl,
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
});
|
||||||
|
console.log('🎮 [PlayerPanel] PanelInfo isPaused changed', {
|
||||||
|
previousIsPaused: previousPanelInfo.current?.isPaused,
|
||||||
|
currentIsPaused: panelInfo.isPaused,
|
||||||
|
videoPlayerExists: !!videoPlayer.current,
|
||||||
|
currentPlayingUrl,
|
||||||
|
shptmBanrTpNm: panelInfo?.shptmBanrTpNm,
|
||||||
|
showId: panelInfo?.showId,
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
source: 'useEffect panelInfo.isPaused',
|
||||||
|
});
|
||||||
|
|
||||||
|
if (videoPlayer.current) {
|
||||||
|
if (panelInfo.isPaused === true) {
|
||||||
|
console.log('🔴 [PlayerPanel] Calling VideoPlayer.pause() due to PanelInfo change');
|
||||||
|
videoPlayer.current.pause();
|
||||||
|
} else if (panelInfo.isPaused === false) {
|
||||||
|
console.log('🟢 [PlayerPanel] Calling VideoPlayer.play() due to PanelInfo change');
|
||||||
|
videoPlayer.current.play();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [panelInfo?.isPaused]);
|
|
||||||
|
previousPanelInfo.current = panelInfo;
|
||||||
|
}, [panelInfo?.isPaused, topPanel?.name, currentPlayingUrl]);
|
||||||
|
|
||||||
|
// VideoPlayer 인스턴스 및 소스 변경 모니터링 (중요 변화만)
|
||||||
|
useEffect(() => {
|
||||||
|
const isOnTop = panel_names.HOME_PANEL === topPanel?.name;
|
||||||
|
const isDetailPanelOnTop = panel_names.DETAIL_PANEL === topPanel?.name;
|
||||||
|
const isVideoSourceChanged = previousVideoSource.current !== currentPlayingUrl;
|
||||||
|
const isOnTopChanged = previousIsOnTop.current !== isOnTop;
|
||||||
|
const isDetailPanelOnTopChanged =
|
||||||
|
previousIsOnTop.current === false && isDetailPanelOnTop === true;
|
||||||
|
const videoPlayerJustCreated = previousVideoSource.current === null && videoPlayer.current;
|
||||||
|
|
||||||
|
if (
|
||||||
|
(isVideoSourceChanged ||
|
||||||
|
isOnTopChanged ||
|
||||||
|
isDetailPanelOnTopChanged ||
|
||||||
|
videoPlayerJustCreated) &&
|
||||||
|
videoPlayer.current
|
||||||
|
) {
|
||||||
|
const changeReason = isDetailPanelOnTopChanged
|
||||||
|
? 'DetailPanel opened'
|
||||||
|
: isVideoSourceChanged
|
||||||
|
? 'Video source changed'
|
||||||
|
: isOnTopChanged
|
||||||
|
? 'Top panel changed'
|
||||||
|
: 'VideoPlayer created';
|
||||||
|
|
||||||
|
console.log('🎬 [PlayerPanel] VideoPlayer state change', {
|
||||||
|
hasVideoPlayer: !!videoPlayer.current,
|
||||||
|
currentPlayingUrl,
|
||||||
|
previousVideoSource: previousVideoSource.current,
|
||||||
|
topPanelName: topPanel?.name,
|
||||||
|
isOnTop,
|
||||||
|
isDetailPanelOnTop,
|
||||||
|
isVideoSourceChanged,
|
||||||
|
isOnTopChanged,
|
||||||
|
isDetailPanelOnTopChanged,
|
||||||
|
videoPlayerJustCreated,
|
||||||
|
changeReason,
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
source: 'useEffect videoPlayer.current',
|
||||||
|
});
|
||||||
|
|
||||||
|
// VideoPlayer 상태 확인 (소스 변경이나 PlayerPanel 생성 시에만)
|
||||||
|
if (isVideoSourceChanged || videoPlayerJustCreated || isDetailPanelOnTopChanged) {
|
||||||
|
const mediaState = videoPlayer.current.getMediaState?.();
|
||||||
|
if (mediaState) {
|
||||||
|
console.log('📊 [PlayerPanel] VideoPlayer current state', {
|
||||||
|
mediaState,
|
||||||
|
videoPlayState,
|
||||||
|
changeReason,
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
source: 'useEffect getMediaState',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
previousVideoSource.current = currentPlayingUrl;
|
||||||
|
previousIsOnTop.current = isOnTop || isDetailPanelOnTop; // 현재 최상위 패널 상태 저장
|
||||||
|
}, [videoPlayer.current, currentPlayingUrl, topPanel?.name]);
|
||||||
|
|
||||||
// Modal 상태 변화 감지 (true → false, false → true)
|
// Modal 상태 변화 감지 (true → false, false → true)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -1130,11 +1267,63 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props
|
|||||||
|
|
||||||
// 최상단 패널이 DetailPanel이고 PlayerPanel에서 진입했는지 확인
|
// 최상단 패널이 DetailPanel이고 PlayerPanel에서 진입했는지 확인
|
||||||
const isTopPanelDetailFromPlayer = useMemo(() => {
|
const isTopPanelDetailFromPlayer = useMemo(() => {
|
||||||
return (
|
const result =
|
||||||
topPanel?.name === panel_names.DETAIL_PANEL &&
|
topPanel?.name === panel_names.DETAIL_PANEL &&
|
||||||
topPanel?.panelInfo?.launchedFromPlayer === true
|
topPanel?.panelInfo?.launchedFromPlayer === true;
|
||||||
);
|
|
||||||
}, [topPanel]);
|
// 🔍 DetailPanel 상태 변화 로깅
|
||||||
|
if (result) {
|
||||||
|
console.log('🎬 [PlayerPanel] DetailPanel is now on top (from Player)', {
|
||||||
|
topPanelName: topPanel?.name,
|
||||||
|
launchedFromPlayer: topPanel?.panelInfo?.launchedFromPlayer,
|
||||||
|
modalPlayerPanelExists: panels.some(
|
||||||
|
(p) => p.name === panel_names.PLAYER_PANEL && p.panelInfo?.modal
|
||||||
|
),
|
||||||
|
currentVideoState: {
|
||||||
|
isPlaying: videoPlayState?.isPlaying,
|
||||||
|
isPaused: videoPlayState?.isPaused,
|
||||||
|
currentTime: videoPlayState?.currentTime,
|
||||||
|
},
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}, [topPanel, panels, videoPlayState]);
|
||||||
|
|
||||||
|
// 🔍 PlayerPanel이 밑에 깔렸을 때 자신의 상태를 업데이트하는 useEffect
|
||||||
|
useEffect(() => {
|
||||||
|
const isDetailPanelOnTop = topPanel?.name === panel_names.DETAIL_PANEL;
|
||||||
|
const isModalPlayerPanel = panelInfo?.modal === true;
|
||||||
|
const isCurrentPanelOnTop = topPanel?.name === panel_names.PLAYER_PANEL;
|
||||||
|
|
||||||
|
// PlayerPanel이 modal이고 밑에 깔렸을 때
|
||||||
|
if (isModalPlayerPanel && !isCurrentPanelOnTop && isDetailPanelOnTop) {
|
||||||
|
console.log('🔴 [PlayerPanel] Self-pausing due to DetailPanel on top', {
|
||||||
|
isDetailPanelOnTop,
|
||||||
|
isModalPlayerPanel,
|
||||||
|
isCurrentPanelOnTop,
|
||||||
|
currentReduxState: videoPlayState,
|
||||||
|
needsPause: videoPlayState?.isPlaying === true || videoPlayState?.isPaused === false,
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
});
|
||||||
|
|
||||||
|
// PlayerPanel 자신의 상태를 일시정지로 업데이트
|
||||||
|
if (videoPlayState?.isPlaying === true || videoPlayState?.isPaused === false) {
|
||||||
|
console.log('🔄 [PlayerPanel] Dispatching self-pause to Redux');
|
||||||
|
dispatch(
|
||||||
|
updateVideoPlayState({
|
||||||
|
isPlaying: false,
|
||||||
|
isPaused: true,
|
||||||
|
currentTime: videoPlayState?.currentTime || 0,
|
||||||
|
duration: videoPlayState?.duration || 0,
|
||||||
|
playbackRate: videoPlayState?.playbackRate || 1,
|
||||||
|
source: 'PlayerPanel-self-pause',
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [topPanel?.name, panelInfo?.modal, videoPlayState, dispatch]);
|
||||||
|
|
||||||
const cannotPlay = useMemo(() => {
|
const cannotPlay = useMemo(() => {
|
||||||
return !isOnTop && topPanel?.name === panel_names.PLAYER_PANEL;
|
return !isOnTop && topPanel?.name === panel_names.PLAYER_PANEL;
|
||||||
@@ -2624,6 +2813,7 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props
|
|||||||
tabContainerVersion={tabContainerVersion}
|
tabContainerVersion={tabContainerVersion}
|
||||||
tabIndexV2={tabIndexV2}
|
tabIndexV2={tabIndexV2}
|
||||||
dispatch={dispatch}
|
dispatch={dispatch}
|
||||||
|
videoPlayState={videoPlayState}
|
||||||
>
|
>
|
||||||
{typeof window === 'object' && window.PalmSystem && (
|
{typeof window === 'object' && window.PalmSystem && (
|
||||||
<source src={currentPlayingUrl} type={videoType} />
|
<source src={currentPlayingUrl} type={videoType} />
|
||||||
|
|||||||
Reference in New Issue
Block a user