[251115] fix: DetailPanel FullScreen Focus Move
🕐 커밋 시간: 2025. 11. 15. 22:03:44 📊 변경 통계: • 총 파일: 17개 • 추가: +573줄 • 삭제: -87줄 📁 추가된 파일: + com.twin.app.shoptime/src/views/PlayerPanel/PlayerOverlay/MediaOverlayContents.jsx 📝 수정된 파일: ~ com.twin.app.shoptime/src/actions/panelActions.js ~ com.twin.app.shoptime/src/components/MediaPlayer/MediaControls.js ~ com.twin.app.shoptime/src/components/VideoPlayer/MediaPlayer.module.less ~ com.twin.app.shoptime/src/components/VideoPlayer/MediaPlayer.v2.jsx ~ com.twin.app.shoptime/src/components/VideoPlayer/VideoPlayer.v3.js ~ com.twin.app.shoptime/src/components/VideoPlayer/VideoPlayer.v3.module.less ~ com.twin.app.shoptime/src/utils/SpotlightIds.js ~ com.twin.app.shoptime/src/views/HomePanel/BestSeller/BestSeller.jsx ~ com.twin.app.shoptime/src/views/HomePanel/EventPopUpBanner/EventPopUpBanner.jsx ~ com.twin.app.shoptime/src/views/HomePanel/HomeBanner/RandomUnit.jsx ~ com.twin.app.shoptime/src/views/HomePanel/PickedForYou/PickedForYou.jsx ~ com.twin.app.shoptime/src/views/HomePanel/SubCategory/SubCategory.jsx ~ com.twin.app.shoptime/src/views/MainView/MainView.jsx ~ com.twin.app.shoptime/src/views/MediaPanel/MediaPanel.v3.jsx ~ com.twin.app.shoptime/src/views/PlayerPanel/PlayerOverlay/PlayerOverlayContents.jsx ~ com.twin.app.shoptime/src/views/SearchPanel/SearchResultsNew/ItemCard.jsx 🔧 함수 변경 내용: 📄 com.twin.app.shoptime/src/actions/panelActions.js (javascript): ✅ Added: updatePanel() 📄 com.twin.app.shoptime/src/components/MediaPlayer/MediaControls.js (javascript): ✅ Added: onSpotlightRight(), onSpotlightUp(), MediaControlsDecoratorHOC(), handleCancel() ❌ Deleted: onSpotlightRight(), onSpotlightUp(), MediaControlsDecoratorHOC(), handleCancel() 📄 com.twin.app.shoptime/src/components/VideoPlayer/MediaPlayer.v2.jsx (javascript): 🔄 Modified: SpotlightContainerDecorator() 📄 com.twin.app.shoptime/src/components/VideoPlayer/VideoPlayer.v3.module.less (unknown): ✅ Added: position() ❌ Deleted: position() 📄 com.twin.app.shoptime/src/views/HomePanel/HomeBanner/RandomUnit.jsx (javascript): 🔄 Modified: SpotlightContainerDecorator() 📄 com.twin.app.shoptime/src/views/HomePanel/SubCategory/SubCategory.jsx (javascript): 🔄 Modified: getExpsOrdByLgCatCd() 📄 com.twin.app.shoptime/src/views/MediaPanel/MediaPanel.v3.jsx (javascript): 🔄 Modified: normalizeModalStyle() 📄 com.twin.app.shoptime/src/views/PlayerPanel/PlayerOverlay/PlayerOverlayContents.jsx (javascript): 🔄 Modified: SpotlightContainerDecorator(), PlayerOverlayContents() 📄 com.twin.app.shoptime/src/views/PlayerPanel/PlayerOverlay/MediaOverlayContents.jsx (javascript): ✅ Added: MediaOverlayContents() 🔧 주요 변경 내용: • 핵심 비즈니스 로직 개선 • UI 컴포넌트 아키텍처 개선 • 공통 유틸리티 함수 최적화
This commit is contained in:
@@ -11,6 +11,8 @@ export const SOURCE_MENUS = {
|
|||||||
HOME_ROLLING_UNIT: 'home_rolling_unit',
|
HOME_ROLLING_UNIT: 'home_rolling_unit',
|
||||||
HOME_EVENT_POPUP: 'home_event_popup',
|
HOME_EVENT_POPUP: 'home_event_popup',
|
||||||
HOME_TODAYS_DEAL: 'home_todays_deal',
|
HOME_TODAYS_DEAL: 'home_todays_deal',
|
||||||
|
SEARCH_RESULT: 'search_result',
|
||||||
|
HOME_GENERAL: 'home_general',
|
||||||
THEMED_PRODUCT: 'themed_product',
|
THEMED_PRODUCT: 'themed_product',
|
||||||
GENERAL_PRODUCT: 'general_product',
|
GENERAL_PRODUCT: 'general_product',
|
||||||
};
|
};
|
||||||
@@ -43,6 +45,417 @@ export const resetPanels = (panels) => ({
|
|||||||
payload: panels,
|
payload: panels,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DetailPanel로 이동하는 공통 액션 함수
|
||||||
|
* @param {Object} params - 이동 파라미터
|
||||||
|
* @param {string} params.patnrId - 파트너 ID
|
||||||
|
* @param {string} params.prdtId - 상품 ID
|
||||||
|
* @param {string} [params.curationId] - 큐레이션 ID (테마 상품인 경우)
|
||||||
|
* @param {string} [params.nowShelf] - 현재 셸프 ID
|
||||||
|
* @param {string} [params.type] - 상품 타입 ('theme' 등)
|
||||||
|
* @param {string} [params.sourceMenu] - 시작 메뉴 (SOURCE_MENUS 상수 사용)
|
||||||
|
* @param {Object} [params.additionalInfo] - 추가 정보
|
||||||
|
* @returns {Function} Redux thunk 함수
|
||||||
|
*/
|
||||||
|
export const navigateToDetail = ({
|
||||||
|
patnrId,
|
||||||
|
prdtId,
|
||||||
|
curationId,
|
||||||
|
nowShelf,
|
||||||
|
type,
|
||||||
|
sourceMenu,
|
||||||
|
additionalInfo = {},
|
||||||
|
}) => {
|
||||||
|
return (dispatch, getState) => {
|
||||||
|
const panelInfo = {
|
||||||
|
patnrId,
|
||||||
|
prdtId,
|
||||||
|
...additionalInfo,
|
||||||
|
};
|
||||||
|
|
||||||
|
// 선택적 파라미터들 추가
|
||||||
|
if (curationId) panelInfo.curationId = curationId;
|
||||||
|
if (nowShelf) panelInfo.nowShelf = nowShelf;
|
||||||
|
if (type) panelInfo.type = type;
|
||||||
|
if (sourceMenu) panelInfo.sourceMenu = sourceMenu;
|
||||||
|
|
||||||
|
// 로깅
|
||||||
|
console.log(`[navigateToDetail] ${sourceMenu || 'unknown'} → DetailPanel`, {
|
||||||
|
patnrId,
|
||||||
|
prdtId,
|
||||||
|
curationId,
|
||||||
|
nowShelf,
|
||||||
|
type,
|
||||||
|
sourceMenu,
|
||||||
|
timestamp: Date.now(),
|
||||||
|
});
|
||||||
|
|
||||||
|
// sourceMenu에 따른 사전 처리
|
||||||
|
switch (sourceMenu) {
|
||||||
|
case SOURCE_MENUS.HOME_BEST_SELLER:
|
||||||
|
case SOURCE_MENUS.HOME_PICKED_FOR_YOU:
|
||||||
|
case SOURCE_MENUS.HOME_SUB_CATEGORY:
|
||||||
|
case SOURCE_MENUS.HOME_EVENT_POPUP:
|
||||||
|
case SOURCE_MENUS.HOME_TODAYS_DEAL:
|
||||||
|
case SOURCE_MENUS.HOME_RANDOM_UNIT:
|
||||||
|
case SOURCE_MENUS.HOME_ROLLING_UNIT:
|
||||||
|
case SOURCE_MENUS.HOME_GENERAL: {
|
||||||
|
// 🔽 모든 HomePanel에서 DetailPanel로 이동 시 HomeBanner modal 비디오 정지
|
||||||
|
const state = getState();
|
||||||
|
const playerPanelInfo = state.panels.panels.find(
|
||||||
|
(p) => p.name === panel_names.PLAYER_PANEL
|
||||||
|
);
|
||||||
|
|
||||||
|
// playerPanel이 없는 경우 비디오 정지 로직 건너뛰기
|
||||||
|
if (!playerPanelInfo) {
|
||||||
|
// 비디오가 없어도 HomePanel 상태 저장
|
||||||
|
dispatch(
|
||||||
|
updatePanel({
|
||||||
|
name: panel_names.HOME_PANEL,
|
||||||
|
panelInfo: {
|
||||||
|
lastSelectedProduct: { patnrId, prdtId },
|
||||||
|
lastActionSource: sourceMenu,
|
||||||
|
...additionalInfo,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
panelInfo.fromHome = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const isCurrentBannerVideoPlaying = playerPanelInfo.panelInfo?.modal !== false;
|
||||||
|
|
||||||
|
// HomeBanner의 modal=true 비디오가 재생 중이면 정지
|
||||||
|
if (isCurrentBannerVideoPlaying) {
|
||||||
|
// 🔽 비디오 상태 저장 후 정지
|
||||||
|
const { finishVideoPreview } = require('./playActions');
|
||||||
|
|
||||||
|
// 비디오 복원을 위한 상태 저장
|
||||||
|
const videoStateToRestore = {
|
||||||
|
...playerPanelInfo.panelInfo,
|
||||||
|
wasPlaying: true,
|
||||||
|
restoreOnBack: true,
|
||||||
|
sourceMenu,
|
||||||
|
timestamp: Date.now(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// HomePanel에 비디오 복원 상태 저장
|
||||||
|
dispatch(
|
||||||
|
updatePanel({
|
||||||
|
name: panel_names.HOME_PANEL,
|
||||||
|
panelInfo: {
|
||||||
|
videoStateToRestore,
|
||||||
|
lastSelectedProduct: { patnrId, prdtId },
|
||||||
|
lastActionSource: sourceMenu,
|
||||||
|
...additionalInfo,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// 비디오 상태 저장 후 정지 (로그는 개발 시 필요 시 주석 해제)
|
||||||
|
|
||||||
|
dispatch(finishVideoPreview());
|
||||||
|
} else {
|
||||||
|
// 비디오가 재생 중이 아니어도 HomePanel 상태 저장
|
||||||
|
dispatch(
|
||||||
|
updatePanel({
|
||||||
|
name: panel_names.HOME_PANEL,
|
||||||
|
panelInfo: {
|
||||||
|
lastSelectedProduct: { patnrId, prdtId },
|
||||||
|
lastActionSource: sourceMenu,
|
||||||
|
...additionalInfo,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// HomePanel 내부 컴포넌트들: 기본 HomePanel 상태 저장
|
||||||
|
dispatch(
|
||||||
|
updatePanel({
|
||||||
|
name: panel_names.HOME_PANEL,
|
||||||
|
panelInfo: {
|
||||||
|
lastSelectedProduct: { patnrId, prdtId },
|
||||||
|
lastActionSource: sourceMenu,
|
||||||
|
...additionalInfo,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
panelInfo.fromHome = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case SOURCE_MENUS.SEARCH_RESULT:
|
||||||
|
// Search: 현재 패널 상태 저장 (updatePanel)
|
||||||
|
if (additionalInfo.searchVal && additionalInfo.currentSpot) {
|
||||||
|
dispatch(
|
||||||
|
updatePanel({
|
||||||
|
name: panel_names.SEARCH_PANEL,
|
||||||
|
panelInfo: {
|
||||||
|
searchVal: additionalInfo.searchVal,
|
||||||
|
currentSpot: additionalInfo.currentSpot,
|
||||||
|
tab: additionalInfo.tab || 0,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
panelInfo.fromSearch = true;
|
||||||
|
panelInfo.searchQuery = additionalInfo.searchVal;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SOURCE_MENUS.THEMED_PRODUCT:
|
||||||
|
// 테마 상품: 별도 처리 필요할 경우
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SOURCE_MENUS.GENERAL_PRODUCT:
|
||||||
|
default:
|
||||||
|
// 일반 상품: 기본 처리
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// DetailPanel push
|
||||||
|
dispatch(
|
||||||
|
pushPanel({
|
||||||
|
name: panel_names.DETAIL_PANEL,
|
||||||
|
panelInfo,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 테마 상품을 위한 DetailPanel 이동 헬퍼 함수
|
||||||
|
* @param {Object} params - 이동 파라미터
|
||||||
|
* @returns {Function} Redux thunk
|
||||||
|
*/
|
||||||
|
export const navigateToThemeDetail = ({
|
||||||
|
patnrId,
|
||||||
|
prdtId,
|
||||||
|
curationId,
|
||||||
|
sourceMenu = SOURCE_MENUS.THEMED_PRODUCT,
|
||||||
|
...additionalInfo
|
||||||
|
}) => {
|
||||||
|
return navigateToDetail({
|
||||||
|
patnrId,
|
||||||
|
prdtId,
|
||||||
|
curationId,
|
||||||
|
type: 'theme',
|
||||||
|
sourceMenu,
|
||||||
|
...additionalInfo,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 홈패널 BestSeller에서 DetailPanel로 이동
|
||||||
|
* @param {Object} params - 상품 정보
|
||||||
|
* @returns {Function} Redux thunk
|
||||||
|
*/
|
||||||
|
export const navigateFromBestSeller = ({ patnrId, prdtId, spotlightId }) => {
|
||||||
|
return navigateToDetail({
|
||||||
|
patnrId,
|
||||||
|
prdtId,
|
||||||
|
nowShelf: spotlightId,
|
||||||
|
sourceMenu: SOURCE_MENUS.HOME_BEST_SELLER,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 홈패널 PickedForYou에서 DetailPanel로 이동
|
||||||
|
* @param {Object} params - 상품 정보
|
||||||
|
* @returns {Function} Redux thunk
|
||||||
|
*/
|
||||||
|
export const navigateFromPickedForYou = ({ patnrId, prdtId, spotlightId }) => {
|
||||||
|
return navigateToDetail({
|
||||||
|
patnrId,
|
||||||
|
prdtId,
|
||||||
|
nowShelf: spotlightId,
|
||||||
|
sourceMenu: SOURCE_MENUS.HOME_PICKED_FOR_YOU,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 홈패널 SubCategory에서 DetailPanel로 이동
|
||||||
|
* @param {Object} params - 상품 정보
|
||||||
|
* @returns {Function} Redux thunk
|
||||||
|
*/
|
||||||
|
export const navigateFromSubCategory = ({ patnrId, prdtId, spotlightId }) => {
|
||||||
|
return navigateToDetail({
|
||||||
|
patnrId,
|
||||||
|
prdtId,
|
||||||
|
nowShelf: spotlightId,
|
||||||
|
sourceMenu: SOURCE_MENUS.HOME_SUB_CATEGORY,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 홈패널 RandomUnit 배너에서 DetailPanel로 이동
|
||||||
|
* @param {Object} params - 상품 정보
|
||||||
|
* @returns {Function} Redux thunk
|
||||||
|
*/
|
||||||
|
export const navigateFromRandomUnit = ({ patnrId, prdtId, curationId, type = 'product' }) => {
|
||||||
|
return navigateToDetail({
|
||||||
|
patnrId,
|
||||||
|
prdtId,
|
||||||
|
curationId,
|
||||||
|
type: type === 'theme' ? 'theme' : undefined,
|
||||||
|
sourceMenu: SOURCE_MENUS.HOME_RANDOM_UNIT,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 홈패널 RollingUnit 배너에서 DetailPanel로 이동
|
||||||
|
* @param {Object} params - 상품 정보
|
||||||
|
* @returns {Function} Redux thunk
|
||||||
|
*/
|
||||||
|
export const navigateFromRollingUnit = ({ patnrId, prdtId, curationId, additionalInfo = {} }) => {
|
||||||
|
return navigateToDetail({
|
||||||
|
patnrId,
|
||||||
|
prdtId,
|
||||||
|
curationId,
|
||||||
|
sourceMenu: SOURCE_MENUS.HOME_ROLLING_UNIT,
|
||||||
|
...additionalInfo,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 홈패널 EventPopUpBanner에서 DetailPanel로 이동
|
||||||
|
* @param {Object} params - 상품 정보
|
||||||
|
* @returns {Function} Redux thunk
|
||||||
|
*/
|
||||||
|
export const navigateFromEventPopup = ({ patnrId, prdtId }) => {
|
||||||
|
return navigateToDetail({
|
||||||
|
patnrId,
|
||||||
|
prdtId,
|
||||||
|
sourceMenu: SOURCE_MENUS.HOME_EVENT_POPUP,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SearchPanel에서 DetailPanel로 이동
|
||||||
|
* @param {Object} params - 검색 및 상품 정보
|
||||||
|
* @returns {Function} Redux thunk
|
||||||
|
*/
|
||||||
|
export const navigateFromSearch = ({
|
||||||
|
patnrId,
|
||||||
|
prdtId,
|
||||||
|
searchQuery,
|
||||||
|
currentSpot,
|
||||||
|
additionalInfo = {},
|
||||||
|
}) => {
|
||||||
|
return navigateToDetail({
|
||||||
|
patnrId,
|
||||||
|
prdtId,
|
||||||
|
sourceMenu: SOURCE_MENUS.SEARCH_RESULT,
|
||||||
|
additionalInfo: {
|
||||||
|
searchVal: searchQuery,
|
||||||
|
currentSpot,
|
||||||
|
tab: 0,
|
||||||
|
...additionalInfo,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HomePanel 일반 클릭에서 DetailPanel로 이동
|
||||||
|
* @param {Object} params - 상품 정보
|
||||||
|
* @returns {Function} Redux thunk
|
||||||
|
*/
|
||||||
|
export const navigateFromHomeGeneral = ({ patnrId, prdtId, additionalInfo = {} }) => {
|
||||||
|
return navigateToDetail({
|
||||||
|
patnrId,
|
||||||
|
prdtId,
|
||||||
|
sourceMenu: SOURCE_MENUS.HOME_GENERAL,
|
||||||
|
additionalInfo,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DetailPanel에서 돌아올 때 비디오 복원 함수
|
||||||
|
* HomePanel에 저장된 비디오 상태를 확인하고 복원
|
||||||
|
* @returns {Function} Redux thunk
|
||||||
|
*/
|
||||||
|
export const restoreVideoOnBack = () => {
|
||||||
|
return (dispatch, getState) => {
|
||||||
|
const state = getState();
|
||||||
|
const panels = state.panels.panels;
|
||||||
|
|
||||||
|
// HomePanel 찾기
|
||||||
|
const homePanel = panels.find((p) => p.name === panel_names.HOME_PANEL);
|
||||||
|
const videoStateToRestore = homePanel?.panelInfo?.videoStateToRestore;
|
||||||
|
|
||||||
|
if (!videoStateToRestore || !videoStateToRestore.restoreOnBack) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 비디오 복원 시작 (로그는 개발 시 필요 시 주석 해제)
|
||||||
|
|
||||||
|
// 비디오 상태 복원
|
||||||
|
const { startVideoPlayerNew } = require('./playActions');
|
||||||
|
|
||||||
|
// 복원할 비디오 정보 추출
|
||||||
|
const restoreInfo = {
|
||||||
|
bannerId: videoStateToRestore.bannerId || videoStateToRestore.playerState?.currentBannerId,
|
||||||
|
patnrId: videoStateToRestore.patnrId,
|
||||||
|
showId: videoStateToRestore.showId,
|
||||||
|
shptmBanrTpNm: videoStateToRestore.shptmBanrTpNm,
|
||||||
|
lgCatCd: videoStateToRestore.lgCatCd,
|
||||||
|
modal: true, // HomeBanner는 항상 modal
|
||||||
|
modalContainerId: videoStateToRestore.modalContainerId,
|
||||||
|
modalClassName: videoStateToRestore.modalClassName,
|
||||||
|
chanId: videoStateToRestore.chanId,
|
||||||
|
};
|
||||||
|
|
||||||
|
// 비디오 재생 시작
|
||||||
|
dispatch(
|
||||||
|
startVideoPlayerNew({
|
||||||
|
...restoreInfo,
|
||||||
|
spotlightDisable: false,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// 복원 상태 정리
|
||||||
|
dispatch(
|
||||||
|
updatePanel({
|
||||||
|
name: panel_names.HOME_PANEL,
|
||||||
|
panelInfo: {
|
||||||
|
...homePanel.panelInfo,
|
||||||
|
videoStateToRestore: {
|
||||||
|
...videoStateToRestore,
|
||||||
|
restoreOnBack: false, // 복원 완료 후 플래그 초기화
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DetailPanel 닫기 시 비디오 복원 확인 함수
|
||||||
|
* DetailPanel 패널이 제거될 때 자동으로 비디오 복원 시도
|
||||||
|
* @returns {Function} Redux thunk
|
||||||
|
*/
|
||||||
|
export const handleDetailPanelCloseWithVideoRestore = () => {
|
||||||
|
return (dispatch, getState) => {
|
||||||
|
const state = getState();
|
||||||
|
const panels = state.panels.panels;
|
||||||
|
|
||||||
|
// 현재 최상단 패널이 DetailPanel인지 확인
|
||||||
|
const topPanel = panels[panels.length - 1];
|
||||||
|
|
||||||
|
if (topPanel?.name === panel_names.DETAIL_PANEL) {
|
||||||
|
// 기존 DetailPanel 닫기 로직 수행
|
||||||
|
dispatch({
|
||||||
|
type: 'POP_PANEL_WITH_VIDEO_RESTORE',
|
||||||
|
payload: panel_names.DETAIL_PANEL,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 비디오 복원 시도 (약간의 지연 후)
|
||||||
|
setTimeout(() => {
|
||||||
|
dispatch(restoreVideoOnBack());
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* [251114] 명시적 포커스 이동
|
* [251114] 명시적 포커스 이동
|
||||||
* Panel의 비동기 작업(useEffect, 타이머 등)이 포커스를 탈취하는 것을 방지
|
* Panel의 비동기 작업(useEffect, 타이머 등)이 포커스를 탈취하는 것을 방지
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -588,6 +588,8 @@
|
|||||||
|
|
||||||
.overlay {
|
.overlay {
|
||||||
.position(@position: absolute, @top: 0, @right: 0, @bottom: 0, @left: 0);
|
.position(@position: absolute, @top: 0, @right: 0, @bottom: 0, @left: 0);
|
||||||
|
pointer-events: auto;
|
||||||
|
z-index: 10;
|
||||||
}
|
}
|
||||||
@keyframes spin {
|
@keyframes spin {
|
||||||
0% {
|
0% {
|
||||||
@@ -714,6 +716,7 @@
|
|||||||
|
|
||||||
.controlsHandleAbove {
|
.controlsHandleAbove {
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
|
z-index: -1;
|
||||||
.position(@position: absolute, @top: 0, @right: 0, @bottom: auto, @left: 0);
|
.position(@position: absolute, @top: 0, @right: 0, @bottom: auto, @left: 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,15 @@
|
|||||||
* - 메모리 효율성 우선
|
* - 메모리 효율성 우선
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { useState, useRef, useEffect, useCallback, useMemo, forwardRef, useImperativeHandle } from 'react';
|
import React, {
|
||||||
|
useState,
|
||||||
|
useRef,
|
||||||
|
useEffect,
|
||||||
|
useCallback,
|
||||||
|
useMemo,
|
||||||
|
forwardRef,
|
||||||
|
useImperativeHandle,
|
||||||
|
} from 'react';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import DurationFmt from 'ilib/lib/DurationFmt';
|
import DurationFmt from 'ilib/lib/DurationFmt';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
@@ -33,7 +41,7 @@ import {
|
|||||||
setMediaControlToggle,
|
setMediaControlToggle,
|
||||||
startMediaAutoClose,
|
startMediaAutoClose,
|
||||||
stopMediaAutoClose,
|
stopMediaAutoClose,
|
||||||
resetMediaAutoClose
|
resetMediaAutoClose,
|
||||||
} from '../../actions/mediaOverlayActions';
|
} from '../../actions/mediaOverlayActions';
|
||||||
|
|
||||||
import css from './MediaPlayer.module.less';
|
import css from './MediaPlayer.module.less';
|
||||||
@@ -49,11 +57,12 @@ const RootContainer = SpotlightContainerDecorator(
|
|||||||
|
|
||||||
// DurationFmt memoization
|
// DurationFmt memoization
|
||||||
const memoGetDurFmt = memoize(
|
const memoGetDurFmt = memoize(
|
||||||
() => new DurationFmt({
|
() =>
|
||||||
length: 'medium',
|
new DurationFmt({
|
||||||
style: 'clock',
|
length: 'medium',
|
||||||
useNative: false,
|
style: 'clock',
|
||||||
})
|
useNative: false,
|
||||||
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
const getDurFmt = () => {
|
const getDurFmt = () => {
|
||||||
@@ -68,7 +77,7 @@ const getDurFmt = () => {
|
|||||||
format: (time) => {
|
format: (time) => {
|
||||||
if (!time || !time.millisecond) return '00:00';
|
if (!time || !time.millisecond) return '00:00';
|
||||||
return secondsToTime(Math.floor(time.millisecond / 1000));
|
return secondsToTime(Math.floor(time.millisecond / 1000));
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -159,7 +168,7 @@ const MediaPlayerV2 = forwardRef((props, ref) => {
|
|||||||
try {
|
try {
|
||||||
// URL 파싱 시도
|
// URL 파싱 시도
|
||||||
const url = new URL(src);
|
const url = new URL(src);
|
||||||
return ['youtube.com', 'youtu.be', 'm.youtube.com'].some(domain =>
|
return ['youtube.com', 'youtu.be', 'm.youtube.com'].some((domain) =>
|
||||||
url.hostname.includes(domain)
|
url.hostname.includes(domain)
|
||||||
);
|
);
|
||||||
} catch {
|
} catch {
|
||||||
@@ -231,65 +240,75 @@ const MediaPlayerV2 = forwardRef((props, ref) => {
|
|||||||
}
|
}
|
||||||
}, [ActualVideoComponent]);
|
}, [ActualVideoComponent]);
|
||||||
|
|
||||||
const handleUpdate = useCallback((ev) => {
|
const handleUpdate = useCallback(
|
||||||
const el = videoRef.current;
|
(ev) => {
|
||||||
if (!el) return;
|
const el = videoRef.current;
|
||||||
|
if (!el) return;
|
||||||
|
|
||||||
const newCurrentTime = el.currentTime || 0;
|
const newCurrentTime = el.currentTime || 0;
|
||||||
const newDuration = el.duration || 0;
|
const newDuration = el.duration || 0;
|
||||||
|
|
||||||
// 상태 업데이트
|
// 상태 업데이트
|
||||||
setCurrentTime(newCurrentTime);
|
setCurrentTime(newCurrentTime);
|
||||||
setDuration(newDuration);
|
setDuration(newDuration);
|
||||||
setPaused(el.paused);
|
setPaused(el.paused);
|
||||||
setLoading(el.loading || false);
|
setLoading(el.loading || false);
|
||||||
setError(el.error || null);
|
setError(el.error || null);
|
||||||
|
|
||||||
// 함수형 업데이트로 stale closure 방지
|
// 함수형 업데이트로 stale closure 방지
|
||||||
setSourceUnavailable((prevUnavailable) =>
|
setSourceUnavailable((prevUnavailable) => (el.loading && prevUnavailable) || el.error);
|
||||||
(el.loading && prevUnavailable) || el.error
|
|
||||||
);
|
|
||||||
|
|
||||||
// Proportion 계산
|
// Proportion 계산
|
||||||
updateProportionLoaded(); // 플랫폼별 계산 함수 호출
|
updateProportionLoaded(); // 플랫폼별 계산 함수 호출
|
||||||
setProportionPlayed(newDuration > 0 ? newCurrentTime / newDuration : 0);
|
setProportionPlayed(newDuration > 0 ? newCurrentTime / newDuration : 0);
|
||||||
|
|
||||||
// 콜백 호출
|
// 콜백 호출
|
||||||
if (ev.type === 'timeupdate' && onTimeUpdate) {
|
if (ev.type === 'timeupdate' && onTimeUpdate) {
|
||||||
onTimeUpdate(ev);
|
onTimeUpdate(ev);
|
||||||
}
|
}
|
||||||
if (ev.type === 'loadeddata' && onLoadedData) {
|
if (ev.type === 'loadeddata' && onLoadedData) {
|
||||||
onLoadedData(ev);
|
onLoadedData(ev);
|
||||||
}
|
}
|
||||||
if (ev.type === 'loadedmetadata' && onLoadedMetadata) {
|
if (ev.type === 'loadedmetadata' && onLoadedMetadata) {
|
||||||
onLoadedMetadata(ev);
|
onLoadedMetadata(ev);
|
||||||
}
|
}
|
||||||
if (ev.type === 'durationchange' && onDurationChange) {
|
if (ev.type === 'durationchange' && onDurationChange) {
|
||||||
onDurationChange(ev);
|
onDurationChange(ev);
|
||||||
}
|
}
|
||||||
}, [onTimeUpdate, onLoadedData, onLoadedMetadata, onDurationChange, updateProportionLoaded]);
|
},
|
||||||
|
[onTimeUpdate, onLoadedData, onLoadedMetadata, onDurationChange, updateProportionLoaded]
|
||||||
|
);
|
||||||
|
|
||||||
const handleEnded = useCallback((e) => {
|
const handleEnded = useCallback(
|
||||||
if (onEnded) {
|
(e) => {
|
||||||
onEnded(e);
|
if (onEnded) {
|
||||||
}
|
onEnded(e);
|
||||||
}, [onEnded]);
|
}
|
||||||
|
},
|
||||||
|
[onEnded]
|
||||||
|
);
|
||||||
|
|
||||||
const handleErrorEvent = useCallback((e) => {
|
const handleErrorEvent = useCallback(
|
||||||
setError(e);
|
(e) => {
|
||||||
if (onError) {
|
setError(e);
|
||||||
onError(e);
|
if (onError) {
|
||||||
}
|
onError(e);
|
||||||
}, [onError]);
|
}
|
||||||
|
},
|
||||||
|
[onError]
|
||||||
|
);
|
||||||
|
|
||||||
// ========== Controls Management ==========
|
// ========== Controls Management ==========
|
||||||
const showControls = useCallback((timeout = 3000) => {
|
const showControls = useCallback(
|
||||||
if (disabled || isModal) return;
|
(timeout = 3000) => {
|
||||||
|
if (disabled || isModal) return;
|
||||||
|
|
||||||
console.log('🎬 [MediaPlayer.v2] showControls called, dispatching setMediaControlShow');
|
console.log('🎬 [MediaPlayer.v2] showControls called, dispatching setMediaControlShow');
|
||||||
dispatch(setMediaControlShow());
|
dispatch(setMediaControlShow());
|
||||||
dispatch(startMediaAutoClose(timeout));
|
dispatch(startMediaAutoClose(timeout));
|
||||||
}, [disabled, isModal, dispatch]);
|
},
|
||||||
|
[disabled, isModal, dispatch]
|
||||||
|
);
|
||||||
|
|
||||||
const hideControls = useCallback(() => {
|
const hideControls = useCallback(() => {
|
||||||
console.log('🎬 [MediaPlayer.v2] hideControls called, dispatching setMediaControlHide');
|
console.log('🎬 [MediaPlayer.v2] hideControls called, dispatching setMediaControlHide');
|
||||||
@@ -351,10 +370,13 @@ const MediaPlayerV2 = forwardRef((props, ref) => {
|
|||||||
}, [currentTime, duration, paused, loading, error, proportionLoaded]);
|
}, [currentTime, duration, paused, loading, error, proportionLoaded]);
|
||||||
|
|
||||||
// ========== Slider Event Handlers ==========
|
// ========== Slider Event Handlers ==========
|
||||||
const handleSliderChange = useCallback(({ value }) => {
|
const handleSliderChange = useCallback(
|
||||||
const time = value * duration;
|
({ value }) => {
|
||||||
seek(time);
|
const time = value * duration;
|
||||||
}, [duration, seek]);
|
seek(time);
|
||||||
|
},
|
||||||
|
[duration, seek]
|
||||||
|
);
|
||||||
|
|
||||||
const handleKnobMove = useCallback((ev) => {
|
const handleKnobMove = useCallback((ev) => {
|
||||||
if (!videoRef.current) return;
|
if (!videoRef.current) return;
|
||||||
@@ -459,17 +481,21 @@ const MediaPlayerV2 = forwardRef((props, ref) => {
|
|||||||
}, [dispatch]);
|
}, [dispatch]);
|
||||||
|
|
||||||
// ========== Imperative Handle (API) ==========
|
// ========== Imperative Handle (API) ==========
|
||||||
useImperativeHandle(ref, () => ({
|
useImperativeHandle(
|
||||||
play,
|
ref,
|
||||||
pause,
|
() => ({
|
||||||
seek,
|
play,
|
||||||
getMediaState,
|
pause,
|
||||||
showControls,
|
seek,
|
||||||
hideControls,
|
getMediaState,
|
||||||
toggleControls,
|
showControls,
|
||||||
areControlsVisible: () => controlsVisible,
|
hideControls,
|
||||||
getVideoNode: () => videoRef.current,
|
toggleControls,
|
||||||
}), [play, pause, seek, getMediaState, showControls, hideControls, toggleControls, controlsVisible]);
|
areControlsVisible: () => controlsVisible,
|
||||||
|
getVideoNode: () => videoRef.current,
|
||||||
|
}),
|
||||||
|
[play, pause, seek, getMediaState, showControls, hideControls, toggleControls, controlsVisible]
|
||||||
|
);
|
||||||
|
|
||||||
// ========== setApiProvider 호출 ==========
|
// ========== setApiProvider 호출 ==========
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -486,7 +512,16 @@ const MediaPlayerV2 = forwardRef((props, ref) => {
|
|||||||
getVideoNode: () => videoRef.current,
|
getVideoNode: () => videoRef.current,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [setApiProvider, play, pause, seek, getMediaState, showControls, hideControls, toggleControls]);
|
}, [
|
||||||
|
setApiProvider,
|
||||||
|
play,
|
||||||
|
pause,
|
||||||
|
seek,
|
||||||
|
getMediaState,
|
||||||
|
showControls,
|
||||||
|
hideControls,
|
||||||
|
toggleControls,
|
||||||
|
]);
|
||||||
|
|
||||||
// ========== Video Props ==========
|
// ========== Video Props ==========
|
||||||
const videoProps = useMemo(() => {
|
const videoProps = useMemo(() => {
|
||||||
@@ -535,7 +570,19 @@ const MediaPlayerV2 = forwardRef((props, ref) => {
|
|||||||
...baseProps,
|
...baseProps,
|
||||||
ref: assignVideoNode,
|
ref: assignVideoNode,
|
||||||
};
|
};
|
||||||
}, [ActualVideoComponent, assignVideoNode, src, paused, loop, muted, handleLoadStart, handleUpdate, handleEnded, handleErrorEvent, reactPlayerConfig]);
|
}, [
|
||||||
|
ActualVideoComponent,
|
||||||
|
assignVideoNode,
|
||||||
|
src,
|
||||||
|
paused,
|
||||||
|
loop,
|
||||||
|
muted,
|
||||||
|
handleLoadStart,
|
||||||
|
handleUpdate,
|
||||||
|
handleEnded,
|
||||||
|
handleErrorEvent,
|
||||||
|
reactPlayerConfig,
|
||||||
|
]);
|
||||||
|
|
||||||
// ========== Spotlight Handler ==========
|
// ========== Spotlight Handler ==========
|
||||||
const handleSpotlightFocus = useCallback(() => {
|
const handleSpotlightFocus = useCallback(() => {
|
||||||
@@ -564,18 +611,13 @@ const MediaPlayerV2 = forwardRef((props, ref) => {
|
|||||||
>
|
>
|
||||||
{/* Video Element */}
|
{/* Video Element */}
|
||||||
{ActualVideoComponent === Media ? (
|
{ActualVideoComponent === Media ? (
|
||||||
<ActualVideoComponent {...videoProps}>
|
<ActualVideoComponent {...videoProps}>{children}</ActualVideoComponent>
|
||||||
{children}
|
|
||||||
</ActualVideoComponent>
|
|
||||||
) : (
|
) : (
|
||||||
<ActualVideoComponent {...videoProps} />
|
<ActualVideoComponent {...videoProps} />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Overlay */}
|
{/* Overlay */}
|
||||||
<Overlay
|
<Overlay bottomControlsVisible={controlsVisible} onClick={handleVideoClick}>
|
||||||
bottomControlsVisible={controlsVisible}
|
|
||||||
onClick={handleVideoClick}
|
|
||||||
>
|
|
||||||
{/* Loading + Thumbnail */}
|
{/* Loading + Thumbnail */}
|
||||||
{loading && thumbnailUrl && (
|
{loading && thumbnailUrl && (
|
||||||
<>
|
<>
|
||||||
@@ -594,12 +636,7 @@ const MediaPlayerV2 = forwardRef((props, ref) => {
|
|||||||
{/* Slider Section */}
|
{/* Slider Section */}
|
||||||
<div className={css.sliderContainer}>
|
<div className={css.sliderContainer}>
|
||||||
{/* Times - Total */}
|
{/* Times - Total */}
|
||||||
<Times
|
<Times className={css.times} noCurrentTime total={duration} formatter={getDurFmt()} />
|
||||||
className={css.times}
|
|
||||||
noCurrentTime
|
|
||||||
total={duration}
|
|
||||||
formatter={getDurFmt()}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{/* Times - Current */}
|
{/* Times - Current */}
|
||||||
<Times
|
<Times
|
||||||
@@ -663,7 +700,7 @@ const MediaPlayerV2 = forwardRef((props, ref) => {
|
|||||||
onSpotlightRight={handleSpotlightFocus}
|
onSpotlightRight={handleSpotlightFocus}
|
||||||
onSpotlightLeft={handleSpotlightFocus}
|
onSpotlightLeft={handleSpotlightFocus}
|
||||||
onClick={handleSpotlightFocus}
|
onClick={handleSpotlightFocus}
|
||||||
spotlightDisabled={controlsVisible || shouldDisableSpotlight}
|
spotlightDisabled={controlsVisible || shouldDisableSpotlight || !isModal}
|
||||||
/>
|
/>
|
||||||
</RootContainer>
|
</RootContainer>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -724,6 +724,7 @@ const VideoPlayerBase = class extends React.Component {
|
|||||||
belowContentsVisible: PropTypes.bool,
|
belowContentsVisible: PropTypes.bool,
|
||||||
tabContainerVersion: PropTypes.number,
|
tabContainerVersion: PropTypes.number,
|
||||||
tabIndexV2: PropTypes.number,
|
tabIndexV2: PropTypes.number,
|
||||||
|
overlayContentsComponent: PropTypes.elementType,
|
||||||
dispatch: PropTypes.func,
|
dispatch: PropTypes.func,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1121,6 +1122,8 @@ const VideoPlayerBase = class extends React.Component {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Spotlight.setPointerMode(false);
|
||||||
|
|
||||||
// this.startDelayedFeedbackHide();
|
// this.startDelayedFeedbackHide();
|
||||||
// this.startDelayedTitleHide();
|
// this.startDelayedTitleHide();
|
||||||
|
|
||||||
@@ -1426,6 +1429,15 @@ const VideoPlayerBase = class extends React.Component {
|
|||||||
this.showControlsFromPointer
|
this.showControlsFromPointer
|
||||||
);
|
);
|
||||||
|
|
||||||
|
onVideoClickCapture = (ev) => {
|
||||||
|
if (!this.state.mediaControlsVisible && !this.props.panelInfo?.modal) {
|
||||||
|
this.activityDetected();
|
||||||
|
this.onVideoClick();
|
||||||
|
ev.stopPropagation();
|
||||||
|
ev.preventDefault();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
handleControlsHandleAboveHoldPulse = () => {
|
handleControlsHandleAboveHoldPulse = () => {
|
||||||
if (shouldJump(this.props, this.state)) {
|
if (shouldJump(this.props, this.state)) {
|
||||||
this.handleJump({
|
this.handleJump({
|
||||||
@@ -1997,6 +2009,11 @@ const VideoPlayerBase = class extends React.Component {
|
|||||||
// Player Interaction events
|
// Player Interaction events
|
||||||
//
|
//
|
||||||
onVideoClick = () => {
|
onVideoClick = () => {
|
||||||
|
console.log('[VideoPlayer] onVideoClick', {
|
||||||
|
controlsVisible: this.state.mediaControlsVisible,
|
||||||
|
panelInfoModal: this.props.panelInfo?.modal,
|
||||||
|
pointerMode: Spotlight.getPointerMode(),
|
||||||
|
});
|
||||||
// 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;
|
||||||
@@ -2012,7 +2029,18 @@ const VideoPlayerBase = class extends React.Component {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const willShowControls = !this.state.mediaControlsVisible;
|
||||||
this.toggleControls();
|
this.toggleControls();
|
||||||
|
if (willShowControls && !this.props.panelInfo?.modal) {
|
||||||
|
this.restoreOverlayFocus();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
restoreOverlayFocus = () => {
|
||||||
|
setTimeout(() => {
|
||||||
|
Spotlight.setPointerMode(false);
|
||||||
|
Spotlight.focus(SpotlightIds.PLAYER_BACK_BUTTON);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
onSliderChange = ({ value }) => {
|
onSliderChange = ({ value }) => {
|
||||||
@@ -2216,6 +2244,7 @@ const VideoPlayerBase = class extends React.Component {
|
|||||||
setSideContentsVisible,
|
setSideContentsVisible,
|
||||||
belowContentsVisible,
|
belowContentsVisible,
|
||||||
tabContainerVersion,
|
tabContainerVersion,
|
||||||
|
overlayContentsComponent,
|
||||||
disclaimer,
|
disclaimer,
|
||||||
liveTotalTime,
|
liveTotalTime,
|
||||||
currentLiveTimeSeconds,
|
currentLiveTimeSeconds,
|
||||||
@@ -2264,6 +2293,7 @@ const VideoPlayerBase = class extends React.Component {
|
|||||||
delete mediaProps.belowContentsVisible;
|
delete mediaProps.belowContentsVisible;
|
||||||
delete mediaProps.tabContainerVersion;
|
delete mediaProps.tabContainerVersion;
|
||||||
delete mediaProps.tabIndexV2;
|
delete mediaProps.tabIndexV2;
|
||||||
|
delete mediaProps.overlayContentsComponent;
|
||||||
|
|
||||||
mediaProps.autoPlay = !noAutoPlay;
|
mediaProps.autoPlay = !noAutoPlay;
|
||||||
mediaProps.className = type !== 'MEDIA' ? css.video : css.media;
|
mediaProps.className = type !== 'MEDIA' ? css.video : css.media;
|
||||||
@@ -2272,6 +2302,9 @@ const VideoPlayerBase = class extends React.Component {
|
|||||||
mediaProps.onLoadStart = this.handleLoadStart;
|
mediaProps.onLoadStart = this.handleLoadStart;
|
||||||
mediaProps.onUpdate = this.handleEvent;
|
mediaProps.onUpdate = this.handleEvent;
|
||||||
mediaProps.ref = this.setVideoRef;
|
mediaProps.ref = this.setVideoRef;
|
||||||
|
if (!panelInfo.modal) {
|
||||||
|
mediaProps.tabIndex = -1;
|
||||||
|
}
|
||||||
|
|
||||||
//yhcho ReactPlayer
|
//yhcho ReactPlayer
|
||||||
if ((typeof window === 'object' && !window.PalmSystem) || isYoutube) {
|
if ((typeof window === 'object' && !window.PalmSystem) || isYoutube) {
|
||||||
@@ -2287,6 +2320,7 @@ const VideoPlayerBase = class extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const controlsAriaProps = this.getControlsAriaProps();
|
const controlsAriaProps = this.getControlsAriaProps();
|
||||||
|
const OverlayContents = overlayContentsComponent || PlayerOverlayContents;
|
||||||
|
|
||||||
let proportionSelection = selection;
|
let proportionSelection = selection;
|
||||||
if (proportionSelection != null && this.state.duration) {
|
if (proportionSelection != null && this.state.duration) {
|
||||||
@@ -2356,6 +2390,7 @@ const VideoPlayerBase = class extends React.Component {
|
|||||||
modalClassName
|
modalClassName
|
||||||
)}
|
)}
|
||||||
onClick={this.activityDetected}
|
onClick={this.activityDetected}
|
||||||
|
onClickCapture={this.onVideoClickCapture}
|
||||||
ref={this.setPlayerRef}
|
ref={this.setPlayerRef}
|
||||||
spotlightDisabled={spotlightDisabled}
|
spotlightDisabled={spotlightDisabled}
|
||||||
spotlightId={spotlightId}
|
spotlightId={spotlightId}
|
||||||
@@ -2439,7 +2474,7 @@ const VideoPlayerBase = class extends React.Component {
|
|||||||
css.controlFeedbackBtnLayer + (this.state.infoVisible ? ' ' + css.lift : '')
|
css.controlFeedbackBtnLayer + (this.state.infoVisible ? ' ' + css.lift : '')
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<PlayerOverlayContents
|
<OverlayContents
|
||||||
playListInfo={playListInfo && playListInfo}
|
playListInfo={playListInfo && playListInfo}
|
||||||
selectedIndex={selectedIndex}
|
selectedIndex={selectedIndex}
|
||||||
onClick={onBackButton}
|
onClick={onBackButton}
|
||||||
@@ -2571,7 +2606,7 @@ const VideoPlayerBase = class extends React.Component {
|
|||||||
onClick={this.onSpotlightFocus}
|
onClick={this.onSpotlightFocus}
|
||||||
selectionKeys={controlsHandleAboveSelectionKeys}
|
selectionKeys={controlsHandleAboveSelectionKeys}
|
||||||
spotlightDisabled={
|
spotlightDisabled={
|
||||||
this.state.mediaControlsVisible || spotlightDisabled || panelInfo.modal
|
this.state.mediaControlsVisible || spotlightDisabled || !panelInfo.modal
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<Announce ref={this.setAnnounceRef} />
|
<Announce ref={this.setAnnounceRef} />
|
||||||
|
|||||||
@@ -594,9 +594,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.overlay {
|
.overlay {
|
||||||
.position(@position: absolute, @top: 0, @right: 0, @bottom: 0, @left: 0);
|
.position(@position: absolute, @top: 0, @right: 0, @bottom: 0, @left: 0);
|
||||||
}
|
pointer-events: auto;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
@keyframes spin {
|
@keyframes spin {
|
||||||
0% {
|
0% {
|
||||||
transform: rotate(0.25turn);
|
transform: rotate(0.25turn);
|
||||||
@@ -720,10 +722,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.controlsHandleAbove {
|
.controlsHandleAbove {
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
.position(@position: absolute, @top: 0, @right: 0, @bottom: auto, @left: 0);
|
z-index: -1;
|
||||||
}
|
.position(@position: absolute, @top: 0, @right: 0, @bottom: auto, @left: 0);
|
||||||
|
}
|
||||||
|
|
||||||
// Skin colors
|
// Skin colors
|
||||||
.applySkins({
|
.applySkins({
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ export const SpotlightIds = {
|
|||||||
PLAYER_TAB_BUTTON: 'playerTabArrow',
|
PLAYER_TAB_BUTTON: 'playerTabArrow',
|
||||||
PLAYER_BACK_BUTTON: 'player-back-button',
|
PLAYER_BACK_BUTTON: 'player-back-button',
|
||||||
PLAYER_SUBTITLE_BUTTON: 'player-subtitlebutton',
|
PLAYER_SUBTITLE_BUTTON: 'player-subtitlebutton',
|
||||||
|
PLAYER_PLAY_BUTTON: 'player-play-button',
|
||||||
|
|
||||||
// searchPanel
|
// searchPanel
|
||||||
SEARCH_THEME: 'search_theme',
|
SEARCH_THEME: 'search_theme',
|
||||||
|
|||||||
@@ -1,28 +1,13 @@
|
|||||||
import React, {
|
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
useCallback,
|
|
||||||
useEffect,
|
|
||||||
useMemo,
|
|
||||||
useState,
|
|
||||||
} from 'react';
|
|
||||||
|
|
||||||
import {
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
useDispatch,
|
|
||||||
useSelector,
|
|
||||||
} from 'react-redux';
|
|
||||||
|
|
||||||
import Spotlight from '@enact/spotlight';
|
import Spotlight from '@enact/spotlight';
|
||||||
import {
|
import { SpotlightContainerDecorator } from '@enact/spotlight/SpotlightContainerDecorator';
|
||||||
SpotlightContainerDecorator,
|
|
||||||
} from '@enact/spotlight/SpotlightContainerDecorator';
|
|
||||||
import Spottable from '@enact/spotlight/Spottable';
|
import Spottable from '@enact/spotlight/Spottable';
|
||||||
|
|
||||||
import {
|
import { pushPanel, updatePanel, navigateFromBestSeller } from '../../../actions/panelActions';
|
||||||
pushPanel,
|
import { navigateToDetailFromHome } from '../../../actions/panelNavigationActions';
|
||||||
updatePanel,
|
|
||||||
} from '../../../actions/panelActions';
|
|
||||||
import {
|
|
||||||
navigateToDetailFromHome,
|
|
||||||
} from '../../../actions/panelNavigationActions';
|
|
||||||
import SectionTitle from '../../../components/SectionTitle/SectionTitle';
|
import SectionTitle from '../../../components/SectionTitle/SectionTitle';
|
||||||
import Tag from '../../../components/TItemCard/Tag';
|
import Tag from '../../../components/TItemCard/Tag';
|
||||||
import TItemCard from '../../../components/TItemCard/TItemCard';
|
import TItemCard from '../../../components/TItemCard/TItemCard';
|
||||||
@@ -30,23 +15,13 @@ import TItemCardNew from '../../../components/TItemCard/TItemCard.new';
|
|||||||
import TScroller from '../../../components/TScroller/TScroller';
|
import TScroller from '../../../components/TScroller/TScroller';
|
||||||
import useScrollReset from '../../../hooks/useScrollReset';
|
import useScrollReset from '../../../hooks/useScrollReset';
|
||||||
import useScrollTo from '../../../hooks/useScrollTo';
|
import useScrollTo from '../../../hooks/useScrollTo';
|
||||||
import {
|
import { LOG_CONTEXT_NAME, LOG_MESSAGE_ID, panel_names } from '../../../utils/Config';
|
||||||
LOG_CONTEXT_NAME,
|
import { $L, scaleW } from '../../../utils/helperMethods';
|
||||||
LOG_MESSAGE_ID,
|
|
||||||
panel_names,
|
|
||||||
} from '../../../utils/Config';
|
|
||||||
import {
|
|
||||||
$L,
|
|
||||||
scaleW,
|
|
||||||
} from '../../../utils/helperMethods';
|
|
||||||
import { SpotlightIds } from '../../../utils/SpotlightIds';
|
import { SpotlightIds } from '../../../utils/SpotlightIds';
|
||||||
import css from './BestSeller.module.less';
|
import css from './BestSeller.module.less';
|
||||||
|
|
||||||
const SpottableComponent = Spottable("div");
|
const SpottableComponent = Spottable('div');
|
||||||
const Container = SpotlightContainerDecorator(
|
const Container = SpotlightContainerDecorator({ enterTo: 'last-focused' }, 'div');
|
||||||
{ enterTo: "last-focused" },
|
|
||||||
"div"
|
|
||||||
);
|
|
||||||
|
|
||||||
const BestSeller = ({
|
const BestSeller = ({
|
||||||
order,
|
order,
|
||||||
@@ -58,25 +33,15 @@ const BestSeller = ({
|
|||||||
shelfTitle,
|
shelfTitle,
|
||||||
}) => {
|
}) => {
|
||||||
const { getScrollTo, scrollLeft } = useScrollTo();
|
const { getScrollTo, scrollLeft } = useScrollTo();
|
||||||
const { handleScrollReset, handleStopScrolling } = useScrollReset(
|
const { handleScrollReset, handleStopScrolling } = useScrollReset(scrollLeft, true);
|
||||||
scrollLeft,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
|
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
const { cursorVisible } = useSelector((state) => state.common.appStatus);
|
const { cursorVisible } = useSelector((state) => state.common.appStatus);
|
||||||
|
|
||||||
const bestSellerDatas = useSelector(
|
const bestSellerDatas = useSelector((state) => state.product.bestSellerData?.bestSeller);
|
||||||
(state) => state.product.bestSellerData?.bestSeller
|
|
||||||
);
|
|
||||||
|
|
||||||
const bestSellerNewDatas = useSelector(
|
|
||||||
(state) =>
|
|
||||||
state.foryou?.recommendInfo?.recommendProduct
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
|
const bestSellerNewDatas = useSelector((state) => state.foryou?.recommendInfo?.recommendProduct);
|
||||||
|
|
||||||
const [drawChk, setDrawChk] = useState(false);
|
const [drawChk, setDrawChk] = useState(false);
|
||||||
const [firstChk, setFirstChk] = useState(0);
|
const [firstChk, setFirstChk] = useState(0);
|
||||||
@@ -84,59 +49,57 @@ const BestSeller = ({
|
|||||||
const [bestInfos, setBestInfos] = useState(null);
|
const [bestInfos, setBestInfos] = useState(null);
|
||||||
const [bestItemNewData, setBestItemNewData] = useState([]);
|
const [bestItemNewData, setBestItemNewData] = useState([]);
|
||||||
|
|
||||||
useEffect(()=>{
|
useEffect(() => {
|
||||||
setBestInfos(
|
setBestInfos(
|
||||||
bestSellerNewDatas?.filter(
|
bestSellerNewDatas?.filter((item) => item.recommendTpCd === 'BESTSELLER') || [] // 기본값으로 빈 배열 설정
|
||||||
(item) => item.recommendTpCd === "BESTSELLER"
|
);
|
||||||
) || [] // 기본값으로 빈 배열 설정
|
}, [bestSellerNewDatas]);
|
||||||
)
|
|
||||||
},[bestSellerNewDatas])
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!bestInfos || bestInfos.length === 0) {
|
if (!bestInfos || bestInfos.length === 0) {
|
||||||
const baseData = bestSellerDatas?.map((item) => ({
|
const baseData =
|
||||||
...item,
|
bestSellerDatas?.map((item) => ({
|
||||||
foryou: false,
|
...item,
|
||||||
})) || [];
|
foryou: false,
|
||||||
|
})) || [];
|
||||||
setBestItemNewData(baseData);
|
setBestItemNewData(baseData);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const recommendedData = bestInfos[0].productInfos?.map((item) => ({
|
const recommendedData =
|
||||||
...item,
|
bestInfos[0].productInfos?.map((item) => ({
|
||||||
foryou: true,
|
...item,
|
||||||
})) || [];
|
foryou: true,
|
||||||
|
})) || [];
|
||||||
const recommendedPrdtIds = new Set(recommendedData.map(item => item.prdtId));
|
|
||||||
|
const recommendedPrdtIds = new Set(recommendedData.map((item) => item.prdtId));
|
||||||
const baseData = bestSellerDatas?.map((item) => ({
|
|
||||||
...item,
|
const baseData =
|
||||||
foryou: recommendedPrdtIds.has(item.prdtId),
|
bestSellerDatas?.map((item) => ({
|
||||||
})) || [];
|
...item,
|
||||||
|
foryou: recommendedPrdtIds.has(item.prdtId),
|
||||||
|
})) || [];
|
||||||
|
|
||||||
setBestItemNewData(baseData);
|
setBestItemNewData(baseData);
|
||||||
}, [bestSellerDatas, bestInfos]);
|
}, [bestSellerDatas, bestInfos]);
|
||||||
|
|
||||||
const orderStyle = useMemo(() => ({ order: order }), [order]);
|
const orderStyle = useMemo(() => ({ order: order }), [order]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setDrawChk(true);
|
setDrawChk(true);
|
||||||
}, [bestSellerNewDatas]);
|
}, [bestSellerNewDatas]);
|
||||||
|
|
||||||
const handleCardClick = useCallback(
|
const handleCardClick = useCallback(
|
||||||
(patnrId, prdtId) => () => {
|
(patnrId, prdtId) => () => {
|
||||||
dispatch(
|
dispatch(
|
||||||
pushPanel({
|
navigateFromBestSeller({
|
||||||
name: panel_names.DETAIL_PANEL,
|
patnrId,
|
||||||
panelInfo: {
|
prdtId,
|
||||||
patnrId: patnrId,
|
spotlightId,
|
||||||
prdtId: prdtId,
|
|
||||||
nowShelf: spotlightId,
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
[dispatch]
|
[dispatch, spotlightId]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleMoreCardClick = useCallback(() => {
|
const handleMoreCardClick = useCallback(() => {
|
||||||
@@ -144,7 +107,7 @@ const BestSeller = ({
|
|||||||
pushPanel({
|
pushPanel({
|
||||||
name: panel_names.TRENDING_NOW_PANEL,
|
name: panel_names.TRENDING_NOW_PANEL,
|
||||||
panelInfo: {
|
panelInfo: {
|
||||||
pageName: "BS",
|
pageName: 'BS',
|
||||||
focusedContainerId: SpotlightIds.TRENDING_NOW_BEST_SELLER,
|
focusedContainerId: SpotlightIds.TRENDING_NOW_BEST_SELLER,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@@ -171,21 +134,18 @@ const BestSeller = ({
|
|||||||
if (firstChk === 0 && itemIndex === 0) {
|
if (firstChk === 0 && itemIndex === 0) {
|
||||||
const c = Spotlight.getCurrent();
|
const c = Spotlight.getCurrent();
|
||||||
if (c) {
|
if (c) {
|
||||||
let cAriaLabel = c.getAttribute("aria-label");
|
let cAriaLabel = c.getAttribute('aria-label');
|
||||||
cAriaLabel = "Best Seller, Heading 1," + cAriaLabel;
|
cAriaLabel = 'Best Seller, Heading 1,' + cAriaLabel;
|
||||||
c.setAttribute("aria-label", cAriaLabel);
|
c.setAttribute('aria-label', cAriaLabel);
|
||||||
}
|
}
|
||||||
setFirstChk(1);
|
setFirstChk(1);
|
||||||
} else if (firstChk === 1 && itemIndex === 0) {
|
} else if (firstChk === 1 && itemIndex === 0) {
|
||||||
const c = Spotlight.getCurrent();
|
const c = Spotlight.getCurrent();
|
||||||
if (c) {
|
if (c) {
|
||||||
let cAriaLabel = c.getAttribute("aria-label");
|
let cAriaLabel = c.getAttribute('aria-label');
|
||||||
if (cAriaLabel) {
|
if (cAriaLabel) {
|
||||||
const newcAriaLabel = cAriaLabel.replace(
|
const newcAriaLabel = cAriaLabel.replace('Best Seller, Heading 1,', '');
|
||||||
"Best Seller, Heading 1,",
|
c.setAttribute('aria-label', newcAriaLabel);
|
||||||
""
|
|
||||||
);
|
|
||||||
c.setAttribute("aria-label", newcAriaLabel);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -219,7 +179,6 @@ const BestSeller = ({
|
|||||||
handleShelfFocus();
|
handleShelfFocus();
|
||||||
}
|
}
|
||||||
}, [handleShelfFocus]);
|
}, [handleShelfFocus]);
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container
|
<Container
|
||||||
@@ -230,7 +189,7 @@ const BestSeller = ({
|
|||||||
onFocus={_handleShelfFocus}
|
onFocus={_handleShelfFocus}
|
||||||
>
|
>
|
||||||
<SectionTitle
|
<SectionTitle
|
||||||
title={$L("BEST SELLER")}
|
title={$L('BEST SELLER')}
|
||||||
data-title-index="homeBestSellerTitle"
|
data-title-index="homeBestSellerTitle"
|
||||||
label="BEST SELLER"
|
label="BEST SELLER"
|
||||||
/>
|
/>
|
||||||
@@ -262,15 +221,15 @@ const BestSeller = ({
|
|||||||
) => {
|
) => {
|
||||||
const rankText =
|
const rankText =
|
||||||
rankOrd === 1
|
rankOrd === 1
|
||||||
? rankOrd + "st"
|
? rankOrd + 'st'
|
||||||
: rankOrd === 2
|
: rankOrd === 2
|
||||||
? rankOrd + "nd"
|
? rankOrd + 'nd'
|
||||||
: rankOrd === 3
|
: rankOrd === 3
|
||||||
? rankOrd + "rd"
|
? rankOrd + 'rd'
|
||||||
: rankOrd + "th";
|
: rankOrd + 'th';
|
||||||
return (
|
return (
|
||||||
<TItemCardNew
|
<TItemCardNew
|
||||||
key={"subItem" + itemIndex}
|
key={'subItem' + itemIndex}
|
||||||
contextName={LOG_CONTEXT_NAME.HOME}
|
contextName={LOG_CONTEXT_NAME.HOME}
|
||||||
messageId={LOG_MESSAGE_ID.SHELF_CLICK}
|
messageId={LOG_MESSAGE_ID.SHELF_CLICK}
|
||||||
order={itemIndex + 1}
|
order={itemIndex + 1}
|
||||||
@@ -291,13 +250,13 @@ const BestSeller = ({
|
|||||||
onBlur={handleBlur(itemIndex)}
|
onBlur={handleBlur(itemIndex)}
|
||||||
onClick={handleCardClick(patnrId, prdtId)}
|
onClick={handleCardClick(patnrId, prdtId)}
|
||||||
offerInfo={offerInfo}
|
offerInfo={offerInfo}
|
||||||
spotlightId={"bestsellerItem" + itemIndex}
|
spotlightId={'bestsellerItem' + itemIndex}
|
||||||
firstLabel={rankText}
|
firstLabel={rankText}
|
||||||
label={itemIndex * 1 + 1 + " of " + bestItemNewData.length}
|
label={itemIndex * 1 + 1 + ' of ' + bestItemNewData.length}
|
||||||
lastLabel=" go to detail, button"
|
lastLabel=" go to detail, button"
|
||||||
euEnrgLblInfos={euEnrgLblInfos}
|
euEnrgLblInfos={euEnrgLblInfos}
|
||||||
>
|
>
|
||||||
{foryou === true && <Tag text={"For You"} />}
|
{foryou === true && <Tag text={'For You'} />}
|
||||||
</TItemCardNew>
|
</TItemCardNew>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -308,7 +267,7 @@ const BestSeller = ({
|
|||||||
<SpottableComponent
|
<SpottableComponent
|
||||||
className={css.displayBox}
|
className={css.displayBox}
|
||||||
onClick={handleMoreCardClick}
|
onClick={handleMoreCardClick}
|
||||||
spotlightId={"bestseller-item-more-btn"}
|
spotlightId={'bestseller-item-more-btn'}
|
||||||
aria-label="See more button"
|
aria-label="See more button"
|
||||||
></SpottableComponent>
|
></SpottableComponent>
|
||||||
</div>
|
</div>
|
||||||
@@ -318,4 +277,4 @@ const BestSeller = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default BestSeller;
|
export default BestSeller;
|
||||||
|
|||||||
@@ -1,57 +1,38 @@
|
|||||||
import React, { useCallback, useEffect, useRef, useState } from "react";
|
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||||
|
|
||||||
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 { clearSMS } from "../../../actions/appDataActions";
|
import { clearSMS } from '../../../actions/appDataActions';
|
||||||
import {
|
import { alertToast, setHidePopup, setShowPopup } from '../../../actions/commonActions';
|
||||||
alertToast,
|
|
||||||
setHidePopup,
|
|
||||||
setShowPopup,
|
|
||||||
} from "../../../actions/commonActions";
|
|
||||||
import {
|
import {
|
||||||
clearGetProductCouponDownload,
|
clearGetProductCouponDownload,
|
||||||
getProductCouponDownload,
|
getProductCouponDownload,
|
||||||
} from "../../../actions/couponActions";
|
} from '../../../actions/couponActions';
|
||||||
import { setEventPopClickInfo } from "../../../actions/eventActions";
|
import { setEventPopClickInfo } from '../../../actions/eventActions';
|
||||||
import { sendLogGNB, sendLogShopByMobile } from "../../../actions/logActions";
|
import { sendLogGNB, sendLogShopByMobile } from '../../../actions/logActions';
|
||||||
import { pushPanel } from "../../../actions/panelActions";
|
import { pushPanel, navigateFromEventPopup } from '../../../actions/panelActions';
|
||||||
import { startVideoPlayer } from "../../../actions/playActions";
|
import { startVideoPlayer } from '../../../actions/playActions';
|
||||||
import MobileSendPopUp from "../../../components/MobileSend/MobileSendPopUp";
|
import MobileSendPopUp from '../../../components/MobileSend/MobileSendPopUp';
|
||||||
import TPopUp from "../../../components/TPopUp/TPopUp";
|
import TPopUp from '../../../components/TPopUp/TPopUp';
|
||||||
import { launchMembershipApp } from "../../../lunaSend";
|
import { launchMembershipApp } from '../../../lunaSend';
|
||||||
import {
|
import { ACTIVE_POPUP, LOG_MENU, LOG_TP_NO, MYINFO_TABS, panel_names } from '../../../utils/Config';
|
||||||
ACTIVE_POPUP,
|
import { $L, formatLocalDateTime } from '../../../utils/helperMethods';
|
||||||
LOG_MENU,
|
import css from '../EventPopUpBanner/EventPopUpBanner.module.less';
|
||||||
LOG_TP_NO,
|
|
||||||
MYINFO_TABS,
|
|
||||||
panel_names,
|
|
||||||
} from "../../../utils/Config";
|
|
||||||
import { $L, formatLocalDateTime } from "../../../utils/helperMethods";
|
|
||||||
import css from "../EventPopUpBanner/EventPopUpBanner.module.less";
|
|
||||||
|
|
||||||
const Container = SpotlightContainerDecorator(
|
const Container = SpotlightContainerDecorator({ enterTo: 'last-focused' }, 'div');
|
||||||
{ enterTo: "last-focused" },
|
|
||||||
"div"
|
|
||||||
);
|
|
||||||
|
|
||||||
export default function EventPopUpBanner() {
|
export default function EventPopUpBanner() {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
const eventPopInfosData = useSelector(
|
const eventPopInfosData = useSelector((state) => state.event.eventData.eventPopInfo);
|
||||||
(state) => state.event.eventData.eventPopInfo
|
|
||||||
);
|
|
||||||
const popupVisible = useSelector((state) => state.common.popup.popupVisible);
|
const popupVisible = useSelector((state) => state.common.popup.popupVisible);
|
||||||
const activePopup = useSelector((state) => state.common.popup.activePopup);
|
const activePopup = useSelector((state) => state.common.popup.activePopup);
|
||||||
const userNumber = useSelector(
|
const userNumber = useSelector((state) => state.common?.appStatus.loginUserData.userNumber);
|
||||||
(state) => state.common?.appStatus.loginUserData.userNumber
|
|
||||||
);
|
|
||||||
const smsSuccess = useSelector((state) => state.appData.sendSms.retCode);
|
const smsSuccess = useSelector((state) => state.appData.sendSms.retCode);
|
||||||
const couponDownloadSuccess = useSelector(
|
const couponDownloadSuccess = useSelector((state) => state.coupon?.couponDownloadSuccess);
|
||||||
(state) => state.coupon?.couponDownloadSuccess
|
|
||||||
);
|
|
||||||
|
|
||||||
const timerRef = useRef();
|
const timerRef = useRef();
|
||||||
const shopByMobileLogRef = useRef(null);
|
const shopByMobileLogRef = useRef(null);
|
||||||
@@ -61,7 +42,7 @@ export default function EventPopUpBanner() {
|
|||||||
const evntTpCd = eventPopInfosData?.evntTpCd;
|
const evntTpCd = eventPopInfosData?.evntTpCd;
|
||||||
const evntNm = eventPopInfosData?.evntNm;
|
const evntNm = eventPopInfosData?.evntNm;
|
||||||
|
|
||||||
const [smsTpCd, setSmsTpCd] = useState("");
|
const [smsTpCd, setSmsTpCd] = useState('');
|
||||||
const [eventPopData, setEventPopData] = useState({});
|
const [eventPopData, setEventPopData] = useState({});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -77,12 +58,12 @@ export default function EventPopUpBanner() {
|
|||||||
|
|
||||||
const handleApply = useCallback(() => {
|
const handleApply = useCallback(() => {
|
||||||
if (eventPopData) {
|
if (eventPopData) {
|
||||||
dispatch(setEventPopClickInfo({ evntApplcnFlag: "Y", evntId }));
|
dispatch(setEventPopClickInfo({ evntApplcnFlag: 'Y', evntId }));
|
||||||
|
|
||||||
switch (evntTpCd) {
|
switch (evntTpCd) {
|
||||||
case "EVT00102": // curation
|
case 'EVT00102': // curation
|
||||||
case "EVT00104": // curation + prize
|
case 'EVT00104': // curation + prize
|
||||||
case "EVT00107": {
|
case 'EVT00107': {
|
||||||
// curation + billing coupon
|
// curation + billing coupon
|
||||||
dispatch(setHidePopup());
|
dispatch(setHidePopup());
|
||||||
dispatch(
|
dispatch(
|
||||||
@@ -96,43 +77,43 @@ export default function EventPopUpBanner() {
|
|||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "EVT00105": {
|
case 'EVT00105': {
|
||||||
// coupon Only
|
// coupon Only
|
||||||
setSmsTpCd("APP00212");
|
setSmsTpCd('APP00212');
|
||||||
dispatch(setShowPopup(ACTIVE_POPUP.smsPopup));
|
dispatch(setShowPopup(ACTIVE_POPUP.smsPopup));
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
befPrice: "",
|
befPrice: '',
|
||||||
curationId: eventPopData?.curationId ?? "",
|
curationId: eventPopData?.curationId ?? '',
|
||||||
curationNm: eventPopData?.curationNm ?? "",
|
curationNm: eventPopData?.curationNm ?? '',
|
||||||
evntId: eventPopData?.evntId ?? "",
|
evntId: eventPopData?.evntId ?? '',
|
||||||
evntNm: eventPopData?.evntNm ?? "",
|
evntNm: eventPopData?.evntNm ?? '',
|
||||||
lastPrice: "",
|
lastPrice: '',
|
||||||
lgCatCd: "",
|
lgCatCd: '',
|
||||||
lgCatNm: "",
|
lgCatNm: '',
|
||||||
liveFlag: "N",
|
liveFlag: 'N',
|
||||||
locDt: formatLocalDateTime(new Date()),
|
locDt: formatLocalDateTime(new Date()),
|
||||||
logTpNo: LOG_TP_NO.SHOP_BY_MOBILE.SHOP_BY_MOBILE,
|
logTpNo: LOG_TP_NO.SHOP_BY_MOBILE.SHOP_BY_MOBILE,
|
||||||
mbphNoFlag: "N",
|
mbphNoFlag: 'N',
|
||||||
patncNm: eventPopData?.patncNm,
|
patncNm: eventPopData?.patncNm,
|
||||||
patnrId: eventPopData?.patnrId,
|
patnrId: eventPopData?.patnrId,
|
||||||
prdtId: "",
|
prdtId: '',
|
||||||
prdtNm: "",
|
prdtNm: '',
|
||||||
revwGrd: "",
|
revwGrd: '',
|
||||||
rewdAplyFlag: "N",
|
rewdAplyFlag: 'N',
|
||||||
shopByMobileFlag: "Y",
|
shopByMobileFlag: 'Y',
|
||||||
shopTpNm: "popevent",
|
shopTpNm: 'popevent',
|
||||||
showId: "",
|
showId: '',
|
||||||
showNm: "",
|
showNm: '',
|
||||||
trmsAgrFlag: "N",
|
trmsAgrFlag: 'N',
|
||||||
tsvFlag: "N",
|
tsvFlag: 'N',
|
||||||
};
|
};
|
||||||
|
|
||||||
dispatch(sendLogShopByMobile(params));
|
dispatch(sendLogShopByMobile(params));
|
||||||
shopByMobileLogRef.current = params;
|
shopByMobileLogRef.current = params;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "EVT00108": {
|
case 'EVT00108': {
|
||||||
// Direct+Billng+Coupon
|
// Direct+Billng+Coupon
|
||||||
if (!userNumber) {
|
if (!userNumber) {
|
||||||
dispatch(setShowPopup(ACTIVE_POPUP.loginPopup));
|
dispatch(setShowPopup(ACTIVE_POPUP.loginPopup));
|
||||||
@@ -152,7 +133,7 @@ export default function EventPopUpBanner() {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (couponDownloadSuccess === 0) {
|
if (couponDownloadSuccess === 0) {
|
||||||
dispatch(alertToast($L("Downloading coupon complete.")));
|
dispatch(alertToast($L('Downloading coupon complete.')));
|
||||||
if (eventPopData && eventPopData?.shptmLnkInfo?.shptmLnkTpCd) {
|
if (eventPopData && eventPopData?.shptmLnkInfo?.shptmLnkTpCd) {
|
||||||
dispatch(setHidePopup());
|
dispatch(setHidePopup());
|
||||||
navigateToLinkByTypeCode();
|
navigateToLinkByTypeCode();
|
||||||
@@ -174,7 +155,7 @@ export default function EventPopUpBanner() {
|
|||||||
|
|
||||||
const handleSkip = useCallback(() => {
|
const handleSkip = useCallback(() => {
|
||||||
if (eventPopInfosData) {
|
if (eventPopInfosData) {
|
||||||
dispatch(setEventPopClickInfo({ evntApplcnFlag: "N", evntId }));
|
dispatch(setEventPopClickInfo({ evntApplcnFlag: 'N', evntId }));
|
||||||
dispatch(setHidePopup());
|
dispatch(setHidePopup());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -185,7 +166,7 @@ export default function EventPopUpBanner() {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
timerRef.current = setTimeout(() => {
|
timerRef.current = setTimeout(() => {
|
||||||
Spotlight.focus("tPopupBtn1");
|
Spotlight.focus('tPopupBtn1');
|
||||||
}, 100);
|
}, 100);
|
||||||
return () => clearTimeout(timerRef.current);
|
return () => clearTimeout(timerRef.current);
|
||||||
}, []);
|
}, []);
|
||||||
@@ -200,23 +181,23 @@ export default function EventPopUpBanner() {
|
|||||||
|
|
||||||
if (eventPopData && eventPopData.shptmLnkInfo) {
|
if (eventPopData && eventPopData.shptmLnkInfo) {
|
||||||
switch (eventPopData.shptmLnkInfo.shptmLnkTpCd) {
|
switch (eventPopData.shptmLnkInfo.shptmLnkTpCd) {
|
||||||
case "EVT00201":
|
case 'EVT00201':
|
||||||
break;
|
break;
|
||||||
case "EVT00202":
|
case 'EVT00202':
|
||||||
dispatch(
|
dispatch(
|
||||||
pushPanel({
|
pushPanel({
|
||||||
name: panel_names.FEATURED_BRANDS_PANEL,
|
name: panel_names.FEATURED_BRANDS_PANEL,
|
||||||
panelInfo: {
|
panelInfo: {
|
||||||
from: "gnb",
|
from: 'gnb',
|
||||||
patnrId: eventPopData.shptmLnkInfo?.lnkPatnrId,
|
patnrId: eventPopData.shptmLnkInfo?.lnkPatnrId,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case "EVT00203":
|
case 'EVT00203':
|
||||||
dispatch(pushPanel({ name: panel_names.TRENDING_NOW_PANEL }));
|
dispatch(pushPanel({ name: panel_names.TRENDING_NOW_PANEL }));
|
||||||
break;
|
break;
|
||||||
case "EVT00204":
|
case 'EVT00204':
|
||||||
pushPanel({
|
pushPanel({
|
||||||
name: panel_names.HOT_PICKS_PANEL,
|
name: panel_names.HOT_PICKS_PANEL,
|
||||||
panelInfo: {
|
panelInfo: {
|
||||||
@@ -225,7 +206,7 @@ export default function EventPopUpBanner() {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case "EVT00205":
|
case 'EVT00205':
|
||||||
dispatch(
|
dispatch(
|
||||||
pushPanel({
|
pushPanel({
|
||||||
name: panel_names.ON_SALE_PANEL,
|
name: panel_names.ON_SALE_PANEL,
|
||||||
@@ -233,7 +214,7 @@ export default function EventPopUpBanner() {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case "EVT00206":
|
case 'EVT00206':
|
||||||
dispatch(
|
dispatch(
|
||||||
pushPanel({
|
pushPanel({
|
||||||
name: panel_names.CATEGORY_PANEL,
|
name: panel_names.CATEGORY_PANEL,
|
||||||
@@ -243,28 +224,25 @@ export default function EventPopUpBanner() {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case "EVT00207":
|
case 'EVT00207':
|
||||||
dispatch(
|
dispatch(
|
||||||
pushPanel({
|
navigateFromEventPopup({
|
||||||
name: panel_names.DETAIL_PANEL,
|
patnrId: eventPopData.shptmLnkInfo?.lnkPatnrId,
|
||||||
panelInfo: {
|
prdtId: eventPopData.shptmLnkInfo?.lnkPrdtId,
|
||||||
patnrId: eventPopData.shptmLnkInfo?.lnkPatnrId,
|
|
||||||
prdtId: eventPopData.shptmLnkInfo?.lnkPrdtId,
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case "EVT00208":
|
case 'EVT00208':
|
||||||
dispatch(
|
dispatch(
|
||||||
startVideoPlayer({
|
startVideoPlayer({
|
||||||
modal: false,
|
modal: false,
|
||||||
patnrId: eventPopData.shptmLnkInfo?.lnkPatnrId,
|
patnrId: eventPopData.shptmLnkInfo?.lnkPatnrId,
|
||||||
showId: eventPopData.shptmLnkInfo?.lnkBrdcId,
|
showId: eventPopData.shptmLnkInfo?.lnkBrdcId,
|
||||||
shptmBanrTpNm: "VOD",
|
shptmBanrTpNm: 'VOD',
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case "EVT00209":
|
case 'EVT00209':
|
||||||
dispatch(
|
dispatch(
|
||||||
pushPanel({
|
pushPanel({
|
||||||
name: panel_names.THEME_CURATION_PANEL,
|
name: panel_names.THEME_CURATION_PANEL,
|
||||||
@@ -274,12 +252,12 @@ export default function EventPopUpBanner() {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case "EVT00210":
|
case 'EVT00210':
|
||||||
dispatch(
|
dispatch(
|
||||||
pushPanel({
|
pushPanel({
|
||||||
name: panel_names.MY_PAGE_PANEL,
|
name: panel_names.MY_PAGE_PANEL,
|
||||||
panelInfo: {
|
panelInfo: {
|
||||||
menuNm: "My Info",
|
menuNm: 'My Info',
|
||||||
menuOrd: 3,
|
menuOrd: 3,
|
||||||
tabForced: MYINFO_TABS.COUPON,
|
tabForced: MYINFO_TABS.COUPON,
|
||||||
},
|
},
|
||||||
@@ -307,8 +285,8 @@ export default function EventPopUpBanner() {
|
|||||||
onClose={handleSkip}
|
onClose={handleSkip}
|
||||||
onClick={handleApply}
|
onClick={handleApply}
|
||||||
hasButton
|
hasButton
|
||||||
button1Text={$L("Apply Now")}
|
button1Text={$L('Apply Now')}
|
||||||
button2Text={$L("Skip")}
|
button2Text={$L('Skip')}
|
||||||
className={css.eventPopup}
|
className={css.eventPopup}
|
||||||
>
|
>
|
||||||
{eventPopInfosData && (
|
{eventPopInfosData && (
|
||||||
@@ -322,10 +300,8 @@ export default function EventPopUpBanner() {
|
|||||||
<MobileSendPopUp
|
<MobileSendPopUp
|
||||||
open={popupVisible}
|
open={popupVisible}
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
title={$L("Send To Mobile")}
|
title={$L('Send To Mobile')}
|
||||||
subTitle={$L(
|
subTitle={$L('Enter your mobile number to receive detailed information via SMS')}
|
||||||
"Enter your mobile number to receive detailed information via SMS"
|
|
||||||
)}
|
|
||||||
smsTpCd={smsTpCd}
|
smsTpCd={smsTpCd}
|
||||||
evntId={eventPopData.evntId}
|
evntId={eventPopData.evntId}
|
||||||
patnrId={eventPopData.patnrId}
|
patnrId={eventPopData.patnrId}
|
||||||
@@ -337,7 +313,7 @@ export default function EventPopUpBanner() {
|
|||||||
kind="textPopup"
|
kind="textPopup"
|
||||||
open={popupVisible}
|
open={popupVisible}
|
||||||
hasText
|
hasText
|
||||||
text={$L("Downloading coupon complete.")}
|
text={$L('Downloading coupon complete.')}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{activePopup === ACTIVE_POPUP.loginPopup && (
|
{activePopup === ACTIVE_POPUP.loginPopup && (
|
||||||
@@ -345,10 +321,10 @@ export default function EventPopUpBanner() {
|
|||||||
kind="textPopup"
|
kind="textPopup"
|
||||||
hasText
|
hasText
|
||||||
open={popupVisible}
|
open={popupVisible}
|
||||||
text={$L("Would you like to sign in?")}
|
text={$L('Would you like to sign in?')}
|
||||||
hasButton
|
hasButton
|
||||||
button1Text={$L("OK")}
|
button1Text={$L('OK')}
|
||||||
button2Text={$L("CANCEL")}
|
button2Text={$L('CANCEL')}
|
||||||
onClick={handleLogin}
|
onClick={handleLogin}
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
></TPopUp>
|
></TPopUp>
|
||||||
|
|||||||
@@ -17,7 +17,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 } from '../../../actions/panelActions';
|
import { pushPanel, navigateFromRandomUnit } from '../../../actions/panelActions';
|
||||||
import {
|
import {
|
||||||
finishVideoPreview,
|
finishVideoPreview,
|
||||||
startVideoPlayer,
|
startVideoPlayer,
|
||||||
@@ -462,17 +462,11 @@ export default function RandomUnit({
|
|||||||
|
|
||||||
// 투데이즈딜 클릭
|
// 투데이즈딜 클릭
|
||||||
const todayDealClick = useCallback(() => {
|
const todayDealClick = useCallback(() => {
|
||||||
if (playerPanelInfo?.modal !== false) {
|
|
||||||
finishAndUnlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
dispatch(
|
dispatch(
|
||||||
pushPanel({
|
navigateFromRandomUnit({
|
||||||
name: panel_names.DETAIL_PANEL,
|
patnrId: randomData.patnrId,
|
||||||
panelInfo: {
|
prdtId: randomData.prdtId,
|
||||||
patnrId: randomData.patnrId,
|
type: 'product',
|
||||||
prdtId: randomData.prdtId,
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -1,41 +1,24 @@
|
|||||||
import React, {
|
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
useCallback,
|
|
||||||
useEffect,
|
|
||||||
useMemo,
|
|
||||||
useState,
|
|
||||||
} from 'react';
|
|
||||||
|
|
||||||
import {
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
useDispatch,
|
|
||||||
useSelector,
|
|
||||||
} from 'react-redux';
|
|
||||||
|
|
||||||
import Spotlight from '@enact/spotlight';
|
import Spotlight from '@enact/spotlight';
|
||||||
import {
|
import { SpotlightContainerDecorator } from '@enact/spotlight/SpotlightContainerDecorator';
|
||||||
SpotlightContainerDecorator,
|
|
||||||
} from '@enact/spotlight/SpotlightContainerDecorator';
|
|
||||||
import Spottable from '@enact/spotlight/Spottable';
|
import Spottable from '@enact/spotlight/Spottable';
|
||||||
|
|
||||||
import { pushPanel } from '../../../actions/panelActions';
|
import { pushPanel, navigateFromPickedForYou } from '../../../actions/panelActions';
|
||||||
import SectionTitle from '../../../components/SectionTitle/SectionTitle';
|
import SectionTitle from '../../../components/SectionTitle/SectionTitle';
|
||||||
import TItemCardNew from '../../../components/TItemCard/TItemCard.new';
|
import TItemCardNew from '../../../components/TItemCard/TItemCard.new';
|
||||||
import TScroller from '../../../components/TScroller/TScroller';
|
import TScroller from '../../../components/TScroller/TScroller';
|
||||||
import useScrollReset from '../../../hooks/useScrollReset';
|
import useScrollReset from '../../../hooks/useScrollReset';
|
||||||
import useScrollTo from '../../../hooks/useScrollTo';
|
import useScrollTo from '../../../hooks/useScrollTo';
|
||||||
import {
|
import { LOG_CONTEXT_NAME, LOG_MESSAGE_ID, panel_names } from '../../../utils/Config';
|
||||||
LOG_CONTEXT_NAME,
|
|
||||||
LOG_MESSAGE_ID,
|
|
||||||
panel_names,
|
|
||||||
} from '../../../utils/Config';
|
|
||||||
import { $L } from '../../../utils/helperMethods';
|
import { $L } from '../../../utils/helperMethods';
|
||||||
import { SpotlightIds } from '../../../utils/SpotlightIds';
|
import { SpotlightIds } from '../../../utils/SpotlightIds';
|
||||||
import css from './PickedForYou.module.less';
|
import css from './PickedForYou.module.less';
|
||||||
|
|
||||||
const SpottableComponent = Spottable("div");
|
const SpottableComponent = Spottable('div');
|
||||||
const Container = SpotlightContainerDecorator(
|
const Container = SpotlightContainerDecorator({ enterTo: 'last-focused' }, 'div');
|
||||||
{ enterTo: "last-focused" },
|
|
||||||
"div"
|
|
||||||
);
|
|
||||||
|
|
||||||
const PickedForYou = ({
|
const PickedForYou = ({
|
||||||
order,
|
order,
|
||||||
@@ -46,36 +29,25 @@ const PickedForYou = ({
|
|||||||
shelfLocation,
|
shelfLocation,
|
||||||
shelfTitle,
|
shelfTitle,
|
||||||
}) => {
|
}) => {
|
||||||
|
|
||||||
const { getScrollTo, scrollLeft } = useScrollTo();
|
const { getScrollTo, scrollLeft } = useScrollTo();
|
||||||
const { handleScrollReset, handleStopScrolling } = useScrollReset(
|
const { handleScrollReset, handleStopScrolling } = useScrollReset(scrollLeft, true);
|
||||||
scrollLeft,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
|
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
const { cursorVisible } = useSelector((state) => state.common.appStatus);
|
const { cursorVisible } = useSelector((state) => state.common.appStatus);
|
||||||
|
|
||||||
const justForYouDatasNew = useSelector(
|
const justForYouDatasNew = useSelector((state) => state.foryou?.recommendInfo?.recommendProduct);
|
||||||
(state) => state.foryou?.recommendInfo?.recommendProduct
|
|
||||||
)
|
|
||||||
|
|
||||||
const [drawChk, setDrawChk] = useState(false);
|
const [drawChk, setDrawChk] = useState(false);
|
||||||
const [firstChk, setFirstChk] = useState(0);
|
const [firstChk, setFirstChk] = useState(0);
|
||||||
|
|
||||||
const [pickedForYou, setPickedForYou] = useState(null);
|
const [pickedForYou, setPickedForYou] = useState(null);
|
||||||
|
|
||||||
const orderStyle = useMemo(() => ({ order: order }), [order]);
|
|
||||||
useEffect(()=>{
|
|
||||||
setPickedForYou(
|
|
||||||
justForYouDatasNew?.filter(
|
|
||||||
(item) => item.recommendTpCd === "PICKEDFORYOU"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
},[justForYouDatasNew])
|
|
||||||
|
|
||||||
|
const orderStyle = useMemo(() => ({ order: order }), [order]);
|
||||||
|
useEffect(() => {
|
||||||
|
setPickedForYou(justForYouDatasNew?.filter((item) => item.recommendTpCd === 'PICKEDFORYOU'));
|
||||||
|
}, [justForYouDatasNew]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setDrawChk(true);
|
setDrawChk(true);
|
||||||
}, [justForYouDatasNew]);
|
}, [justForYouDatasNew]);
|
||||||
@@ -83,17 +55,14 @@ const PickedForYou = ({
|
|||||||
const handleCardClick = useCallback(
|
const handleCardClick = useCallback(
|
||||||
(patnrId, prdtId) => () => {
|
(patnrId, prdtId) => () => {
|
||||||
dispatch(
|
dispatch(
|
||||||
pushPanel({
|
navigateFromPickedForYou({
|
||||||
name: panel_names.DETAIL_PANEL,
|
patnrId,
|
||||||
panelInfo: {
|
prdtId,
|
||||||
patnrId: patnrId,
|
spotlightId,
|
||||||
prdtId: prdtId,
|
|
||||||
nowShelf: spotlightId,
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
[dispatch]
|
[dispatch, spotlightId]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleMoreCardClick = useCallback(() => {
|
const handleMoreCardClick = useCallback(() => {
|
||||||
@@ -101,7 +70,7 @@ const PickedForYou = ({
|
|||||||
pushPanel({
|
pushPanel({
|
||||||
name: panel_names.TRENDING_NOW_PANEL,
|
name: panel_names.TRENDING_NOW_PANEL,
|
||||||
panelInfo: {
|
panelInfo: {
|
||||||
pageName: "BS",
|
pageName: 'BS',
|
||||||
focusedContainerId: SpotlightIds.TRENDING_NOW_BEST_SELLER,
|
focusedContainerId: SpotlightIds.TRENDING_NOW_BEST_SELLER,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@@ -128,21 +97,18 @@ const PickedForYou = ({
|
|||||||
if (firstChk === 0 && itemIndex === 0) {
|
if (firstChk === 0 && itemIndex === 0) {
|
||||||
const c = Spotlight.getCurrent();
|
const c = Spotlight.getCurrent();
|
||||||
if (c) {
|
if (c) {
|
||||||
let cAriaLabel = c.getAttribute("aria-label");
|
let cAriaLabel = c.getAttribute('aria-label');
|
||||||
cAriaLabel = "Best Seller, Heading 1," + cAriaLabel;
|
cAriaLabel = 'Best Seller, Heading 1,' + cAriaLabel;
|
||||||
c.setAttribute("aria-label", cAriaLabel);
|
c.setAttribute('aria-label', cAriaLabel);
|
||||||
}
|
}
|
||||||
setFirstChk(1);
|
setFirstChk(1);
|
||||||
} else if (firstChk === 1 && itemIndex === 0) {
|
} else if (firstChk === 1 && itemIndex === 0) {
|
||||||
const c = Spotlight.getCurrent();
|
const c = Spotlight.getCurrent();
|
||||||
if (c) {
|
if (c) {
|
||||||
let cAriaLabel = c.getAttribute("aria-label");
|
let cAriaLabel = c.getAttribute('aria-label');
|
||||||
if (cAriaLabel) {
|
if (cAriaLabel) {
|
||||||
const newcAriaLabel = cAriaLabel.replace(
|
const newcAriaLabel = cAriaLabel.replace('Best Seller, Heading 1,', '');
|
||||||
"Best Seller, Heading 1,",
|
c.setAttribute('aria-label', newcAriaLabel);
|
||||||
""
|
|
||||||
);
|
|
||||||
c.setAttribute("aria-label", newcAriaLabel);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -186,7 +152,7 @@ const PickedForYou = ({
|
|||||||
onFocus={_handleShelfFocus}
|
onFocus={_handleShelfFocus}
|
||||||
>
|
>
|
||||||
<SectionTitle
|
<SectionTitle
|
||||||
title={$L("PICKED FOR YOU")}
|
title={$L('PICKED FOR YOU')}
|
||||||
data-title-index="homeBestSellerTitle"
|
data-title-index="homeBestSellerTitle"
|
||||||
label="PICKED FOR YOU"
|
label="PICKED FOR YOU"
|
||||||
/>
|
/>
|
||||||
@@ -198,26 +164,26 @@ const PickedForYou = ({
|
|||||||
noScrollByWheel
|
noScrollByWheel
|
||||||
>
|
>
|
||||||
{pickedForYou &&
|
{pickedForYou &&
|
||||||
pickedForYou?.[0] &&
|
pickedForYou?.[0] &&
|
||||||
pickedForYou?.[0].productInfos.map(
|
pickedForYou?.[0].productInfos.map(
|
||||||
(
|
(
|
||||||
{
|
{
|
||||||
prdtId,
|
prdtId,
|
||||||
imgUrl,
|
imgUrl,
|
||||||
priceInfo,
|
priceInfo,
|
||||||
prdtNm,
|
prdtNm,
|
||||||
patnrId,
|
patnrId,
|
||||||
offerInfo,
|
offerInfo,
|
||||||
brndNm,
|
brndNm,
|
||||||
patncNm,
|
patncNm,
|
||||||
catNm,
|
catNm,
|
||||||
euEnrgLblInfos
|
euEnrgLblInfos,
|
||||||
},
|
},
|
||||||
itemIndex
|
itemIndex
|
||||||
) => {
|
) => {
|
||||||
return (
|
return (
|
||||||
<TItemCardNew
|
<TItemCardNew
|
||||||
key={"subItem" + itemIndex}
|
key={'subItem' + itemIndex}
|
||||||
contextName={LOG_CONTEXT_NAME.HOME}
|
contextName={LOG_CONTEXT_NAME.HOME}
|
||||||
messageId={LOG_MESSAGE_ID.SHELF_CLICK}
|
messageId={LOG_MESSAGE_ID.SHELF_CLICK}
|
||||||
order={itemIndex + 1}
|
order={itemIndex + 1}
|
||||||
@@ -230,14 +196,14 @@ const PickedForYou = ({
|
|||||||
imageAlt={prdtId}
|
imageAlt={prdtId}
|
||||||
imageSource={imgUrl}
|
imageSource={imgUrl}
|
||||||
priceInfo={priceInfo}
|
priceInfo={priceInfo}
|
||||||
productName={prdtNm}
|
productName={prdtNm}
|
||||||
productId={prdtId}
|
productId={prdtId}
|
||||||
onFocus={handleFocus(itemIndex)}
|
onFocus={handleFocus(itemIndex)}
|
||||||
onBlur={handleBlur(itemIndex)}
|
onBlur={handleBlur(itemIndex)}
|
||||||
onClick={handleCardClick(patnrId, prdtId)}
|
onClick={handleCardClick(patnrId, prdtId)}
|
||||||
offerInfo={offerInfo}
|
offerInfo={offerInfo}
|
||||||
spotlightId={"bestsellerItem" + itemIndex}
|
spotlightId={'bestsellerItem' + itemIndex}
|
||||||
label={itemIndex * 1 + 1 + " of " + pickedForYou?.[0].productInfos.length}
|
label={itemIndex * 1 + 1 + ' of ' + pickedForYou?.[0].productInfos.length}
|
||||||
lastLabel=" go to detail, button"
|
lastLabel=" go to detail, button"
|
||||||
euEnrgLblInfos={euEnrgLblInfos}
|
euEnrgLblInfos={euEnrgLblInfos}
|
||||||
/>
|
/>
|
||||||
@@ -250,7 +216,7 @@ const PickedForYou = ({
|
|||||||
<SpottableComponent
|
<SpottableComponent
|
||||||
className={css.displayBox}
|
className={css.displayBox}
|
||||||
onClick={handleMoreCardClick}
|
onClick={handleMoreCardClick}
|
||||||
spotlightId={"bestseller-item-more-btn"}
|
spotlightId={'bestseller-item-more-btn'}
|
||||||
aria-label="See more button"
|
aria-label="See more button"
|
||||||
></SpottableComponent>
|
></SpottableComponent>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,46 +1,28 @@
|
|||||||
import React, {
|
import React, { memo, useCallback, useEffect, useState } from 'react';
|
||||||
memo,
|
|
||||||
useCallback,
|
|
||||||
useEffect,
|
|
||||||
useState,
|
|
||||||
} from 'react';
|
|
||||||
|
|
||||||
import {
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
useDispatch,
|
|
||||||
useSelector,
|
|
||||||
} from 'react-redux';
|
|
||||||
|
|
||||||
import {
|
import { SpotlightContainerDecorator } from '@enact/spotlight/SpotlightContainerDecorator';
|
||||||
SpotlightContainerDecorator,
|
|
||||||
} from '@enact/spotlight/SpotlightContainerDecorator';
|
|
||||||
import Spottable from '@enact/spotlight/Spottable';
|
import Spottable from '@enact/spotlight/Spottable';
|
||||||
import { setContainerLastFocusedElement } from '@enact/spotlight/src/container';
|
import { setContainerLastFocusedElement } from '@enact/spotlight/src/container';
|
||||||
|
|
||||||
import { sendLogCuration } from '../../../actions/logActions';
|
import { sendLogCuration } from '../../../actions/logActions';
|
||||||
import { getSubCategory } from '../../../actions/mainActions';
|
import { getSubCategory } from '../../../actions/mainActions';
|
||||||
import { pushPanel } from '../../../actions/panelActions';
|
import { pushPanel, navigateFromSubCategory } from '../../../actions/panelActions';
|
||||||
import Tag from '../../../components/TItemCard/Tag';
|
import Tag from '../../../components/TItemCard/Tag';
|
||||||
import TItemCardNew from '../../../components/TItemCard/TItemCard.new';
|
import TItemCardNew from '../../../components/TItemCard/TItemCard.new';
|
||||||
import TScroller from '../../../components/TScroller/TScroller';
|
import TScroller from '../../../components/TScroller/TScroller';
|
||||||
import usePrevious from '../../../hooks/usePrevious';
|
import usePrevious from '../../../hooks/usePrevious';
|
||||||
import useScrollReset from '../../../hooks/useScrollReset';
|
import useScrollReset from '../../../hooks/useScrollReset';
|
||||||
import useScrollTo from '../../../hooks/useScrollTo';
|
import useScrollTo from '../../../hooks/useScrollTo';
|
||||||
import {
|
import { LOG_CONTEXT_NAME, LOG_MESSAGE_ID, LOG_TP_NO, panel_names } from '../../../utils/Config';
|
||||||
LOG_CONTEXT_NAME,
|
|
||||||
LOG_MESSAGE_ID,
|
|
||||||
LOG_TP_NO,
|
|
||||||
panel_names,
|
|
||||||
} from '../../../utils/Config';
|
|
||||||
import { SpotlightIds } from '../../../utils/SpotlightIds';
|
import { SpotlightIds } from '../../../utils/SpotlightIds';
|
||||||
import CategoryNav from '../../HomePanel/SubCategory/CategoryNav/CategoryNav';
|
import CategoryNav from '../../HomePanel/SubCategory/CategoryNav/CategoryNav';
|
||||||
import css from '../../HomePanel/SubCategory/SubCategory.module.less';
|
import css from '../../HomePanel/SubCategory/SubCategory.module.less';
|
||||||
|
|
||||||
const SpottableComponent = Spottable("div");
|
const SpottableComponent = Spottable('div');
|
||||||
const Container = SpotlightContainerDecorator({ enterTo: null }, "div");
|
const Container = SpotlightContainerDecorator({ enterTo: null }, 'div');
|
||||||
const ContainerBasic = SpotlightContainerDecorator(
|
const ContainerBasic = SpotlightContainerDecorator({ enterTo: 'last-focused' }, 'div');
|
||||||
{ enterTo: "last-focused" },
|
|
||||||
"div"
|
|
||||||
);
|
|
||||||
|
|
||||||
const getExpsOrdByLgCatCd = (array, value) => {
|
const getExpsOrdByLgCatCd = (array, value) => {
|
||||||
const expsOrd = array.findIndex(({ lgCatCd }) => value === lgCatCd) + 1;
|
const expsOrd = array.findIndex(({ lgCatCd }) => value === lgCatCd) + 1;
|
||||||
@@ -58,21 +40,12 @@ export default memo(function SubCategory({
|
|||||||
}) {
|
}) {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const { getScrollTo, scrollLeft } = useScrollTo();
|
const { getScrollTo, scrollLeft } = useScrollTo();
|
||||||
const { handleScrollReset, handleStopScrolling } = useScrollReset(
|
const { handleScrollReset, handleStopScrolling } = useScrollReset(scrollLeft, false);
|
||||||
scrollLeft,
|
|
||||||
false
|
|
||||||
);
|
|
||||||
|
|
||||||
const categoryInfos = useSelector(
|
const categoryInfos = useSelector((state) => state.home.menuData?.data?.homeCategory);
|
||||||
(state) => state.home.menuData?.data?.homeCategory
|
const categoryItemInfos = useSelector((state) => state.main.subCategoryData?.categoryItemInfos);
|
||||||
);
|
|
||||||
const categoryItemInfos = useSelector(
|
|
||||||
(state) => state.main.subCategoryData?.categoryItemInfos
|
|
||||||
);
|
|
||||||
|
|
||||||
const foruItemInfos = useSelector(
|
const foruItemInfos = useSelector((state) => state.main.recommendProduct[0]?.productInfos);
|
||||||
(state) => state.main.recommendProduct[0]?.productInfos
|
|
||||||
);
|
|
||||||
|
|
||||||
const nowMenu = useSelector((state) => state.common.menu.nowMenu);
|
const nowMenu = useSelector((state) => state.common.menu.nowMenu);
|
||||||
|
|
||||||
@@ -110,9 +83,9 @@ export default memo(function SubCategory({
|
|||||||
getSubCategory(
|
getSubCategory(
|
||||||
{
|
{
|
||||||
lgCatCd: currentLgCatCd,
|
lgCatCd: currentLgCatCd,
|
||||||
pageSize: "10",
|
pageSize: '10',
|
||||||
tabType: "CAT00102",
|
tabType: 'CAT00102',
|
||||||
filterType: "CAT00202",
|
filterType: 'CAT00202',
|
||||||
recommendIncFlag: 'Y',
|
recommendIncFlag: 'Y',
|
||||||
},
|
},
|
||||||
1
|
1
|
||||||
@@ -124,7 +97,7 @@ export default memo(function SubCategory({
|
|||||||
}, [currentLgCatCd, dispatch, firstChk]);
|
}, [currentLgCatCd, dispatch, firstChk]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!nowMenuRef.current || !nowMenuRef.current.startsWith("Home")) {
|
if (!nowMenuRef.current || !nowMenuRef.current.startsWith('Home')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -179,40 +152,47 @@ export default memo(function SubCategory({
|
|||||||
const handleCardClick = useCallback(
|
const handleCardClick = useCallback(
|
||||||
(patnrId, prdtId) => () => {
|
(patnrId, prdtId) => () => {
|
||||||
dispatch(
|
dispatch(
|
||||||
pushPanel({
|
navigateFromSubCategory({
|
||||||
name: panel_names.DETAIL_PANEL,
|
patnrId,
|
||||||
panelInfo: { patnrId, prdtId, nowShelf: spotlightId },
|
prdtId,
|
||||||
|
spotlightId,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
[dispatch, spotlightId]
|
[dispatch, spotlightId]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleScrollRight = useCallback((e) => {
|
const handleScrollRight = useCallback(
|
||||||
const container = e.currentTarget?.parentNode;
|
(e) => {
|
||||||
const x = container.scrollWidth - container.clientWidth;
|
const container = e.currentTarget?.parentNode;
|
||||||
|
const x = container.scrollWidth - container.clientWidth;
|
||||||
|
|
||||||
setTimeout(() => scrollLeft({ x, animate: true }));
|
setTimeout(() => scrollLeft({ x, animate: true }));
|
||||||
}, [scrollLeft]);
|
},
|
||||||
|
[scrollLeft]
|
||||||
|
);
|
||||||
|
|
||||||
const handleMoreCardClick = useCallback((e) => {
|
const handleMoreCardClick = useCallback(
|
||||||
const lgCatCd = e.currentTarget?.getAttribute("data-catcd-num");
|
(e) => {
|
||||||
const lgCatNm = e.currentTarget?.getAttribute("data-catcd-nm");
|
const lgCatCd = e.currentTarget?.getAttribute('data-catcd-num');
|
||||||
const tab = 0;
|
const lgCatNm = e.currentTarget?.getAttribute('data-catcd-nm');
|
||||||
|
const tab = 0;
|
||||||
|
|
||||||
if (lgCatCd && lgCatNm) {
|
if (lgCatCd && lgCatNm) {
|
||||||
dispatch(
|
dispatch(
|
||||||
pushPanel({
|
pushPanel({
|
||||||
name: panel_names.CATEGORY_PANEL,
|
name: panel_names.CATEGORY_PANEL,
|
||||||
panelInfo: {
|
panelInfo: {
|
||||||
lgCatCd,
|
lgCatCd,
|
||||||
lgCatNm,
|
lgCatNm,
|
||||||
tab,
|
tab,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}, [dispatch]);
|
},
|
||||||
|
[dispatch]
|
||||||
|
);
|
||||||
|
|
||||||
const _handleItemFocus = useCallback(() => {
|
const _handleItemFocus = useCallback(() => {
|
||||||
if (handleItemFocus) {
|
if (handleItemFocus) {
|
||||||
@@ -228,35 +208,34 @@ export default memo(function SubCategory({
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!foruItemInfos || foruItemInfos.length === 0) {
|
if (!foruItemInfos || foruItemInfos.length === 0) {
|
||||||
const baseData = categoryItemInfos?.subCatItemList?.map((item) => ({
|
const baseData =
|
||||||
...item,
|
categoryItemInfos?.subCatItemList?.map((item) => ({
|
||||||
foryou: false,
|
...item,
|
||||||
})) || [];
|
foryou: false,
|
||||||
|
})) || [];
|
||||||
setCategoryItemNewData(baseData);
|
setCategoryItemNewData(baseData);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const recommendedData = foruItemInfos?.map((item) => ({
|
const recommendedData =
|
||||||
...item,
|
foruItemInfos?.map((item) => ({
|
||||||
foryou: true,
|
...item,
|
||||||
})) || [];
|
foryou: true,
|
||||||
|
})) || [];
|
||||||
const recommendedPrdtIds = new Set(recommendedData.map(item => item.prdtId));
|
|
||||||
|
const recommendedPrdtIds = new Set(recommendedData.map((item) => item.prdtId));
|
||||||
const baseData = categoryItemInfos?.subCatItemList?.map((item) => ({
|
|
||||||
...item,
|
const baseData =
|
||||||
foryou: recommendedPrdtIds.has(item.prdtId),
|
categoryItemInfos?.subCatItemList?.map((item) => ({
|
||||||
})) || [];
|
...item,
|
||||||
|
foryou: recommendedPrdtIds.has(item.prdtId),
|
||||||
|
})) || [];
|
||||||
|
|
||||||
setCategoryItemNewData([...baseData]);
|
setCategoryItemNewData([...baseData]);
|
||||||
}, [categoryItemInfos?.subCatItemList, foruItemInfos]);
|
}, [categoryItemInfos?.subCatItemList, foruItemInfos]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container
|
<Container spotlightId={spotlightId} data-wheel-point onFocus={_handleShelfFocus}>
|
||||||
spotlightId={spotlightId}
|
|
||||||
data-wheel-point
|
|
||||||
onFocus={_handleShelfFocus}
|
|
||||||
>
|
|
||||||
<CategoryNav
|
<CategoryNav
|
||||||
categoryInfos={categoryInfos}
|
categoryInfos={categoryInfos}
|
||||||
currentCategoryCode={currentLgCatCd}
|
currentCategoryCode={currentLgCatCd}
|
||||||
@@ -293,7 +272,7 @@ export default memo(function SubCategory({
|
|||||||
) => {
|
) => {
|
||||||
return (
|
return (
|
||||||
<TItemCardNew
|
<TItemCardNew
|
||||||
key={"subItem" + itemIndex}
|
key={'subItem' + itemIndex}
|
||||||
contextName={LOG_CONTEXT_NAME.HOME}
|
contextName={LOG_CONTEXT_NAME.HOME}
|
||||||
messageId={LOG_MESSAGE_ID.SHELF_CLICK}
|
messageId={LOG_MESSAGE_ID.SHELF_CLICK}
|
||||||
catNm={categoryItemInfos?.catNm}
|
catNm={categoryItemInfos?.catNm}
|
||||||
@@ -314,16 +293,11 @@ export default memo(function SubCategory({
|
|||||||
offerInfo={offerInfo}
|
offerInfo={offerInfo}
|
||||||
data-catcd-num={currentLgCatCd}
|
data-catcd-num={currentLgCatCd}
|
||||||
data-catcd-nm={currentLgCatNm}
|
data-catcd-nm={currentLgCatNm}
|
||||||
label={
|
label={itemIndex * 1 + 1 + ' of ' + (categoryItemNewData?.length || 0)}
|
||||||
itemIndex * 1 +
|
|
||||||
1 +
|
|
||||||
" of " +
|
|
||||||
(categoryItemNewData?.length || 0)
|
|
||||||
}
|
|
||||||
lastLabel=" go to detail, button"
|
lastLabel=" go to detail, button"
|
||||||
euEnrgLblInfos={euEnrgLblInfos}
|
euEnrgLblInfos={euEnrgLblInfos}
|
||||||
>
|
>
|
||||||
{foryou === true && <Tag text={"For You"} />}
|
{foryou === true && <Tag text={'For You'} />}
|
||||||
</TItemCardNew>
|
</TItemCardNew>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -333,7 +307,7 @@ export default memo(function SubCategory({
|
|||||||
<SpottableComponent
|
<SpottableComponent
|
||||||
className={css.displayBox}
|
className={css.displayBox}
|
||||||
onClick={handleMoreCardClick}
|
onClick={handleMoreCardClick}
|
||||||
spotlightId={"home-cate-more-btn-" + currentLgCatCd}
|
spotlightId={'home-cate-more-btn-' + currentLgCatCd}
|
||||||
onFocus={_handleItemFocus}
|
onFocus={_handleItemFocus}
|
||||||
data-catcd-num={currentLgCatCd}
|
data-catcd-num={currentLgCatCd}
|
||||||
data-catcd-nm={currentLgCatNm}
|
data-catcd-nm={currentLgCatNm}
|
||||||
@@ -345,4 +319,4 @@ export default memo(function SubCategory({
|
|||||||
</ContainerBasic>
|
</ContainerBasic>
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -186,6 +186,10 @@ export default function MainView({ className, initService }) {
|
|||||||
const isHomeOnTop = useMemo(() => {
|
const isHomeOnTop = useMemo(() => {
|
||||||
return !mainIndex && (panels.length <= 0 || (panels.length === 1 && panels[0].panelInfo.modal));
|
return !mainIndex && (panels.length <= 0 || (panels.length === 1 && panels[0].panelInfo.modal));
|
||||||
}, [mainIndex, panels]);
|
}, [mainIndex, panels]);
|
||||||
|
const hasDetailPanel = useMemo(
|
||||||
|
() => panels.some((panel) => panel?.name === Config.panel_names.DETAIL_PANEL),
|
||||||
|
[panels]
|
||||||
|
);
|
||||||
|
|
||||||
const onPreImageLoadComplete = useCallback(() => {
|
const onPreImageLoadComplete = useCallback(() => {
|
||||||
// console.log('MainView onPreImageLoadComplete');
|
// console.log('MainView onPreImageLoadComplete');
|
||||||
@@ -257,6 +261,7 @@ export default function MainView({ className, initService }) {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{(isHomeOnTop ||
|
{(isHomeOnTop ||
|
||||||
|
hasDetailPanel ||
|
||||||
(panels.length === 1 &&
|
(panels.length === 1 &&
|
||||||
(panels[0]?.name === Config.panel_names.PLAYER_PANEL ||
|
(panels[0]?.name === Config.panel_names.PLAYER_PANEL ||
|
||||||
panels[0]?.name === Config.panel_names.PLAYER_PANEL_NEW ||
|
panels[0]?.name === Config.panel_names.PLAYER_PANEL_NEW ||
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ import TPopUp from '../../components/TPopUp/TPopUp';
|
|||||||
import Media from '../../components/VideoPlayer/Media';
|
import Media from '../../components/VideoPlayer/Media';
|
||||||
import TReactPlayer from '../../components/VideoPlayer/TReactPlayer';
|
import TReactPlayer from '../../components/VideoPlayer/TReactPlayer';
|
||||||
import { VideoPlayer } from '../../components/VideoPlayer/VideoPlayer.v3';
|
import { VideoPlayer } from '../../components/VideoPlayer/VideoPlayer.v3';
|
||||||
|
import MediaOverlayContents from '../PlayerPanel/PlayerOverlay/MediaOverlayContents';
|
||||||
import usePrevious from '../../hooks/usePrevious';
|
import usePrevious from '../../hooks/usePrevious';
|
||||||
import useWhyDidYouUpdate from '../../hooks/useWhyDidYouUpdate';
|
import useWhyDidYouUpdate from '../../hooks/useWhyDidYouUpdate';
|
||||||
import * as Config from '../../utils/Config';
|
import * as Config from '../../utils/Config';
|
||||||
@@ -2244,6 +2245,7 @@ const MediaPanel = React.forwardRef(
|
|||||||
spotlightId={
|
spotlightId={
|
||||||
panelInfo.modal ? 'modal-video-player' : panelInfo.modalContainerId || spotlightId
|
panelInfo.modal ? 'modal-video-player' : panelInfo.modalContainerId || spotlightId
|
||||||
}
|
}
|
||||||
|
overlayContentsComponent={MediaOverlayContents}
|
||||||
handleIndicatorDownClick={handleIndicatorDownClick}
|
handleIndicatorDownClick={handleIndicatorDownClick}
|
||||||
handleIndicatorUpClick={handleIndicatorUpClick}
|
handleIndicatorUpClick={handleIndicatorUpClick}
|
||||||
onError={mediainfoHandler}
|
onError={mediainfoHandler}
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import PlayerOverlayContents from './PlayerOverlayContents';
|
||||||
|
|
||||||
|
function MediaOverlayContents(props) {
|
||||||
|
return <PlayerOverlayContents {...props} forceShowMediaOverlay />;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default MediaOverlayContents;
|
||||||
@@ -37,11 +37,11 @@ function PlayerOverlayContents({
|
|||||||
handleIndicatorDownClick,
|
handleIndicatorDownClick,
|
||||||
tabContainerVersion,
|
tabContainerVersion,
|
||||||
tabIndexV2,
|
tabIndexV2,
|
||||||
|
forceShowMediaOverlay = false,
|
||||||
}) {
|
}) {
|
||||||
const cntry_cd = useSelector((state) => state.common.httpHeader?.cntry_cd);
|
const cntry_cd = useSelector((state) => state.common.httpHeader?.cntry_cd);
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const onClickBack = (ev) => {
|
const onClickBack = (ev) => {
|
||||||
|
|
||||||
// TabContainerV2가 표시된 상태에서 백버튼 클릭 시 이벤트 버블링 방지
|
// TabContainerV2가 표시된 상태에서 백버튼 클릭 시 이벤트 버블링 방지
|
||||||
// (Overlay의 onClick으로 전파되어 toggleControls()가 호출되는 것을 막음)
|
// (Overlay의 onClick으로 전파되어 toggleControls()가 호출되는 것을 막음)
|
||||||
if (tabContainerVersion === 2 && belowContentsVisible) {
|
if (tabContainerVersion === 2 && belowContentsVisible) {
|
||||||
@@ -107,11 +107,15 @@ function PlayerOverlayContents({
|
|||||||
if (type === 'LIVE') {
|
if (type === 'LIVE') {
|
||||||
return Spotlight.focus('videoIndicator-down-button');
|
return Spotlight.focus('videoIndicator-down-button');
|
||||||
}
|
}
|
||||||
Spotlight.focus('videoPlayer_mediaControls');
|
return Spotlight.focus(SpotlightIds.PLAYER_PLAY_BUTTON);
|
||||||
},
|
},
|
||||||
[type]
|
[type]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const onSpotlightMoveSubtitleButton = useCallback(() => {
|
||||||
|
return Spotlight.focus('player-subtitlebutton');
|
||||||
|
}, []);
|
||||||
|
|
||||||
const onSpotlightMoveSlider = useCallback(
|
const onSpotlightMoveSlider = useCallback(
|
||||||
(e) => {
|
(e) => {
|
||||||
if (type === 'VOD') {
|
if (type === 'VOD') {
|
||||||
@@ -154,9 +158,41 @@ function PlayerOverlayContents({
|
|||||||
return Spotlight.focus(SpotlightIds.PLAYER_BACK_BUTTON);
|
return Spotlight.focus(SpotlightIds.PLAYER_BACK_BUTTON);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const handleOverlayKeyDownCapture = useCallback(
|
||||||
|
(ev) => {
|
||||||
|
const currentId = Spotlight.getCurrent()?.getAttribute('data-spotlight-id');
|
||||||
|
if (ev.keyCode === 40 && currentId === SpotlightIds.PLAYER_BACK_BUTTON) {
|
||||||
|
ev.preventDefault();
|
||||||
|
ev.stopPropagation();
|
||||||
|
return onSpotlightMoveMediaButton(ev);
|
||||||
|
}
|
||||||
|
if (ev.keyCode === 39 && currentId === SpotlightIds.PLAYER_BACK_BUTTON) {
|
||||||
|
ev.preventDefault();
|
||||||
|
ev.stopPropagation();
|
||||||
|
return onSpotlightMoveSubtitleButton(ev);
|
||||||
|
}
|
||||||
|
if (ev.keyCode === 37 && currentId === SpotlightIds.PLAYER_SUBTITLE_BUTTON) {
|
||||||
|
ev.preventDefault();
|
||||||
|
ev.stopPropagation();
|
||||||
|
return onSpotlightMoveMediaButton(ev);
|
||||||
|
}
|
||||||
|
if (ev.keyCode === 38 && currentId === SpotlightIds.PLAYER_PLAY_BUTTON) {
|
||||||
|
ev.preventDefault();
|
||||||
|
ev.stopPropagation();
|
||||||
|
return onSpotlightMoveBackButton();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[onSpotlightMoveBackButton, onSpotlightMoveMediaButton, onSpotlightMoveSubtitleButton]
|
||||||
|
);
|
||||||
|
|
||||||
|
const shouldShowExtendedControls = useMemo(
|
||||||
|
() => forceShowMediaOverlay || type !== 'MEDIA',
|
||||||
|
[forceShowMediaOverlay, type]
|
||||||
|
);
|
||||||
|
|
||||||
const currentSideButtonStatus = useMemo(() => {
|
const currentSideButtonStatus = useMemo(() => {
|
||||||
if (
|
if (
|
||||||
type !== 'MEDIA' &&
|
shouldShowExtendedControls &&
|
||||||
!panelInfo?.modal &&
|
!panelInfo?.modal &&
|
||||||
!sideContentsVisible &&
|
!sideContentsVisible &&
|
||||||
tabContainerVersion === 1
|
tabContainerVersion === 1
|
||||||
@@ -164,7 +200,7 @@ function PlayerOverlayContents({
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}, [type, panelInfo, sideContentsVisible, tabContainerVersion]);
|
}, [shouldShowExtendedControls, panelInfo, sideContentsVisible, tabContainerVersion]);
|
||||||
|
|
||||||
const noLiveContentsVisible = useMemo(() => {
|
const noLiveContentsVisible = useMemo(() => {
|
||||||
if (!Array.isArray(playListInfo) || playListInfo.length === 0) {
|
if (!Array.isArray(playListInfo) || playListInfo.length === 0) {
|
||||||
@@ -181,8 +217,8 @@ function PlayerOverlayContents({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Container className={css.overlayContainer}>
|
<Container className={css.overlayContainer} onKeyDownCapture={handleOverlayKeyDownCapture}>
|
||||||
{type !== 'MEDIA' && playListInfo.length > 1 && noLiveContentsVisible && (
|
{shouldShowExtendedControls && playListInfo.length > 1 && noLiveContentsVisible && (
|
||||||
<>
|
<>
|
||||||
<div className={css.indicatorUpButton}>
|
<div className={css.indicatorUpButton}>
|
||||||
<SpottableBtn
|
<SpottableBtn
|
||||||
@@ -232,7 +268,7 @@ function PlayerOverlayContents({
|
|||||||
setSideContentsVisible={setSideContentsVisible}
|
setSideContentsVisible={setSideContentsVisible}
|
||||||
sideContentsVisible={sideContentsVisible}
|
sideContentsVisible={sideContentsVisible}
|
||||||
onSpotlightLeft={
|
onSpotlightLeft={
|
||||||
type !== 'MEDIA' && playListInfo.length < 2 && onSpotlightMoveBackButton
|
shouldShowExtendedControls && playListInfo.length < 2 && onSpotlightMoveBackButton
|
||||||
}
|
}
|
||||||
videoType={type}
|
videoType={type}
|
||||||
/>
|
/>
|
||||||
@@ -247,8 +283,10 @@ function PlayerOverlayContents({
|
|||||||
)}
|
)}
|
||||||
onClick={handleSubtitleOnClick}
|
onClick={handleSubtitleOnClick}
|
||||||
spotlightId="player-subtitlebutton"
|
spotlightId="player-subtitlebutton"
|
||||||
onSpotlightUp={onSpotlightMoveTabButton}
|
onSpotlightUp={onSpotlightMoveBackButton}
|
||||||
onSpotlightLeft={onSpotlightMoveMediaButton}
|
onSpotlightLeft={onSpotlightMoveBackButton}
|
||||||
|
onSpotlightRight={onSpotlightMoveMediaButton}
|
||||||
|
onSpotlightDown={onSpotlightMoveMediaButton}
|
||||||
aria-label="Caption"
|
aria-label="Caption"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -263,6 +301,8 @@ function PlayerOverlayContents({
|
|||||||
? onSpotlightMoveBelowTab
|
? onSpotlightMoveBelowTab
|
||||||
: onSpotlightMoveMediaButton
|
: onSpotlightMoveMediaButton
|
||||||
}
|
}
|
||||||
|
onSpotlightRight={onSpotlightMoveSubtitleButton}
|
||||||
|
onSpotlightUp={onSpotlightMoveSubtitleButton}
|
||||||
aria-label="Video Player Close"
|
aria-label="Video Player Close"
|
||||||
ref={backBtnRef}
|
ref={backBtnRef}
|
||||||
/>
|
/>
|
||||||
@@ -312,7 +352,8 @@ const propsAreEqual = (prev, next) => {
|
|||||||
prev.sideContentsVisible === next.sideContentsVisible &&
|
prev.sideContentsVisible === next.sideContentsVisible &&
|
||||||
prev.belowContentsVisible === next.belowContentsVisible &&
|
prev.belowContentsVisible === next.belowContentsVisible &&
|
||||||
prev.tabContainerVersion === next.tabContainerVersion &&
|
prev.tabContainerVersion === next.tabContainerVersion &&
|
||||||
prev.tabIndexV2 === next.tabIndexV2
|
prev.tabIndexV2 === next.tabIndexV2 &&
|
||||||
|
prev.forceShowMediaOverlay === next.forceShowMediaOverlay
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -4,11 +4,7 @@ import { useDispatch } from 'react-redux';
|
|||||||
|
|
||||||
import Spotlight from '@enact/spotlight';
|
import Spotlight from '@enact/spotlight';
|
||||||
|
|
||||||
import {
|
import { pushPanel, updatePanel, navigateFromSearch } from '../../../actions/panelActions';
|
||||||
pushPanel,
|
|
||||||
updatePanel,
|
|
||||||
} from '../../../actions/panelActions';
|
|
||||||
import { navigateToDetailPanel } from '../../../actions/panelNavigationActions';
|
|
||||||
import TItemCardNew from '../../../components/TItemCard/TItemCard.new';
|
import TItemCardNew from '../../../components/TItemCard/TItemCard.new';
|
||||||
import TScroller from '../../../components/TScroller/TScroller';
|
import TScroller from '../../../components/TScroller/TScroller';
|
||||||
import { panel_names } from '../../../utils/Config';
|
import { panel_names } from '../../../utils/Config';
|
||||||
@@ -49,14 +45,16 @@ const ItemCard = ({ onClick, itemInfo, searchQuery }) => {
|
|||||||
onClick(ev);
|
onClick(ev);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 순차 네비게이션 사용 (Chrome 68 호환)
|
// 통합된 navigateToDetail 사용
|
||||||
dispatch(navigateToDetailPanel(
|
dispatch(
|
||||||
patnrId,
|
navigateFromSearch({
|
||||||
prdtId,
|
patnrId,
|
||||||
searchQuery,
|
prdtId,
|
||||||
currentSpot,
|
searchQuery,
|
||||||
{ tab: 0 }
|
currentSpot,
|
||||||
));
|
additionalInfo: { tab: 0 },
|
||||||
|
})
|
||||||
|
);
|
||||||
},
|
},
|
||||||
[onClick, dispatch, searchQuery]
|
[onClick, dispatch, searchQuery]
|
||||||
);
|
);
|
||||||
@@ -65,7 +63,7 @@ const ItemCard = ({ onClick, itemInfo, searchQuery }) => {
|
|||||||
<>
|
<>
|
||||||
<TScroller className={css.container} spotlightId={SpotlightIds.SEARCH_ITEM}>
|
<TScroller className={css.container} spotlightId={SpotlightIds.SEARCH_ITEM}>
|
||||||
{itemInfo.map((item, index) => {
|
{itemInfo.map((item, index) => {
|
||||||
const { thumbnail, title, dcPrice, price, soldout, contentId,euEnrgLblInfos } = item;
|
const { thumbnail, title, dcPrice, price, soldout, contentId, euEnrgLblInfos } = item;
|
||||||
const tokens = contentId && contentId.split('_');
|
const tokens = contentId && contentId.split('_');
|
||||||
const patnrId = tokens?.[4] || '';
|
const patnrId = tokens?.[4] || '';
|
||||||
const prdtId = tokens?.[5] || '';
|
const prdtId = tokens?.[5] || '';
|
||||||
|
|||||||
Reference in New Issue
Block a user