video player panel overlay
This commit is contained in:
@@ -1,8 +1,16 @@
|
|||||||
import { types } from "./actionTypes";
|
import { types } from "./actionTypes";
|
||||||
|
|
||||||
export const pushPanel = (panel) => ({
|
/*
|
||||||
|
name: panel_names.PLAYER_PANEL,
|
||||||
|
panelInfo: {
|
||||||
|
modal: true //only for video player
|
||||||
|
etc...
|
||||||
|
},
|
||||||
|
*/
|
||||||
|
export const pushPanel = (panel, duplicatable=false) => ({
|
||||||
type: types.PUSH_PANEL,
|
type: types.PUSH_PANEL,
|
||||||
payload: panel,
|
payload: panel,
|
||||||
|
duplicatable: duplicatable
|
||||||
});
|
});
|
||||||
|
|
||||||
export const popPanel = (panelName) => ({
|
export const popPanel = (panelName) => ({
|
||||||
|
|||||||
@@ -1,7 +1,55 @@
|
|||||||
|
import Spotlight from '@enact/spotlight';
|
||||||
import { URLS } from '../api/apiConfig';
|
import { URLS } from '../api/apiConfig';
|
||||||
import { TAxios } from '../api/TAxios';
|
import { TAxios } from '../api/TAxios';
|
||||||
|
import { panel_names } from '../utils/Config';
|
||||||
import { types } from './actionTypes';
|
import { types } from './actionTypes';
|
||||||
|
import { popPanel, pushPanel, updatePanel } from './panelActions';
|
||||||
|
|
||||||
|
|
||||||
|
//yhcho
|
||||||
|
/*
|
||||||
|
dispatch(startVideoPreview({
|
||||||
|
patnrId: randomData.patnrId,
|
||||||
|
showId: randomData.showId,
|
||||||
|
shptmBanrTpNm: randomData.shptmBanrTpNm,
|
||||||
|
lgCatCd: randomData.lgCatCd,
|
||||||
|
modal: true,
|
||||||
|
modalContainerRef: videoModalContainerRef.current, //to calc width, height, left, top
|
||||||
|
modalClassName:css.videoModal
|
||||||
|
}));
|
||||||
|
|
||||||
|
modalClassName: modal more class info and checking it's launched from preview mode
|
||||||
|
*/
|
||||||
|
let startVideoTimer = null;
|
||||||
|
//start modal mode
|
||||||
|
//start Full -> modal mode
|
||||||
|
export const startVideoPlayer = ({ modal, modalContainerId, modalClassName, ...rest }) => (dispatch, getState) => {
|
||||||
|
const panels = getState().panels.panels;
|
||||||
|
const topPanel = panels[panels.length -1];
|
||||||
|
let panelWorkingAction = pushPanel;
|
||||||
|
if(topPanel && topPanel.name === panel_names.PLAYER_PANEL){
|
||||||
|
panelWorkingAction = updatePanel;
|
||||||
|
}
|
||||||
|
dispatch(panelWorkingAction({
|
||||||
|
name: panel_names.PLAYER_PANEL,
|
||||||
|
panelInfo: {
|
||||||
|
modal,
|
||||||
|
modalContainerId,
|
||||||
|
modalClassName,
|
||||||
|
...rest
|
||||||
|
}
|
||||||
|
}, true));
|
||||||
|
if(modal && modalContainerId){
|
||||||
|
Spotlight.focus(modalContainerId);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
export const finishVideoPreview = () => (dispatch, getState) => {
|
||||||
|
const panels = getState().panels.panels;
|
||||||
|
const topPanel = panels[panels.length -1];
|
||||||
|
if(topPanel && topPanel.name === panel_names.PLAYER_PANEL && topPanel.panelInfo.modal){
|
||||||
|
dispatch(popPanel());
|
||||||
|
}
|
||||||
|
};
|
||||||
// 채팅 로그 가져오기 IF-LGSP-371
|
// 채팅 로그 가져오기 IF-LGSP-371
|
||||||
export const getChatLog =
|
export const getChatLog =
|
||||||
({ patnrId, showId }) =>
|
({ patnrId, showId }) =>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
.size(@w: 100%, @h: 100vh);
|
.size(@w: 100%, @h: 100vh);
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
color: black;
|
color: black;
|
||||||
|
position: absolute; //yhcho
|
||||||
> section {
|
> section {
|
||||||
font-family: @baseFont;
|
font-family: @baseFont;
|
||||||
color: @COLOR_GRAY03;
|
color: @COLOR_GRAY03;
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import React, { useCallback, useRef, useMemo } from "react";
|
|||||||
var handledMediaEventsMap =
|
var handledMediaEventsMap =
|
||||||
['onReady', 'onStart', 'onPlay', 'onProgress', 'onDuration', 'onPause', 'onBuffer', 'onBufferEnd', 'onSeek', 'onEnded', 'onError'];
|
['onReady', 'onStart', 'onPlay', 'onProgress', 'onDuration', 'onPause', 'onBuffer', 'onBufferEnd', 'onSeek', 'onEnded', 'onError'];
|
||||||
|
|
||||||
export default function TReactPlayer({mediaEventsMap=handledMediaEventsMap, videoRef, ...rest}) {
|
export default function TReactPlayer({mediaEventsMap=handledMediaEventsMap, videoRef, url, ...rest}) {
|
||||||
const playerRef = useRef(null);
|
const playerRef = useRef(null);
|
||||||
|
|
||||||
const handleEvent = useCallback((type)=> (ev)=> {
|
const handleEvent = useCallback((type)=> (ev)=> {
|
||||||
@@ -55,6 +55,7 @@ export default function TReactPlayer({mediaEventsMap=handledMediaEventsMap, vide
|
|||||||
return (
|
return (
|
||||||
<ReactPlayer
|
<ReactPlayer
|
||||||
ref={playerRef}
|
ref={playerRef}
|
||||||
|
url={url}
|
||||||
progressInterval={1000}
|
progressInterval={1000}
|
||||||
{...handledMediaEvents}
|
{...handledMediaEvents}
|
||||||
{...rest}
|
{...rest}
|
||||||
|
|||||||
@@ -732,6 +732,7 @@ const VideoPlayerBase = class extends React.Component {
|
|||||||
introTime: PropTypes.number,
|
introTime: PropTypes.number,
|
||||||
onClickSkipIntro: PropTypes.func,
|
onClickSkipIntro: PropTypes.func,
|
||||||
onIntroDisabled: PropTypes.func,
|
onIntroDisabled: PropTypes.func,
|
||||||
|
modalClassName: PropTypes.any,
|
||||||
src: PropTypes.string, //for ReactPlayer
|
src: PropTypes.string, //for ReactPlayer
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1388,6 +1389,7 @@ const VideoPlayerBase = class extends React.Component {
|
|||||||
handleEvent = () => {
|
handleEvent = () => {
|
||||||
const el = this.video;
|
const el = this.video;
|
||||||
if(!el){
|
if(!el){
|
||||||
|
console.log('yhcho VideoPlayer no el ');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const updatedState = {
|
const updatedState = {
|
||||||
@@ -1446,7 +1448,7 @@ const VideoPlayerBase = class extends React.Component {
|
|||||||
currentTime: this.state.currentTime,
|
currentTime: this.state.currentTime,
|
||||||
duration: this.state.duration,
|
duration: this.state.duration,
|
||||||
paused: this.state.paused,
|
paused: this.state.paused,
|
||||||
playbackRate: this.video.playbackRate,
|
playbackRate: this.video?.playbackRate,
|
||||||
proportionLoaded: this.state.proportionLoaded,
|
proportionLoaded: this.state.proportionLoaded,
|
||||||
proportionPlayed: this.state.proportionPlayed,
|
proportionPlayed: this.state.proportionPlayed,
|
||||||
};
|
};
|
||||||
@@ -2040,6 +2042,7 @@ const VideoPlayerBase = class extends React.Component {
|
|||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
className,
|
className,
|
||||||
|
modalClassName,
|
||||||
disabled,
|
disabled,
|
||||||
infoComponents,
|
infoComponents,
|
||||||
backButton,
|
backButton,
|
||||||
@@ -2141,7 +2144,7 @@ const VideoPlayerBase = class extends React.Component {
|
|||||||
return (
|
return (
|
||||||
<RootContainer
|
<RootContainer
|
||||||
className={
|
className={
|
||||||
css.videoPlayer + " enact-fit" + (className ? " " + className : "")
|
classNames(css.videoPlayer + " enact-fit" + (className ? " " + className : ""), modalClassName)
|
||||||
}
|
}
|
||||||
onClick={this.activityDetected}
|
onClick={this.activityDetected}
|
||||||
ref={this.setPlayerRef}
|
ref={this.setPlayerRef}
|
||||||
|
|||||||
@@ -60,7 +60,9 @@
|
|||||||
background-size: cover;
|
background-size: cover;
|
||||||
border-radius: 20.8125rem;
|
border-radius: 20.8125rem;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
margin: 490px auto;
|
// margin: 490px auto;
|
||||||
|
left: calc(50% - 50px);
|
||||||
|
top: calc(50% - 50px);
|
||||||
animation: none 1.25s linear infinite;
|
animation: none 1.25s linear infinite;
|
||||||
animation-name: spin;
|
animation-name: spin;
|
||||||
// animation-play-state: paused;
|
// animation-play-state: paused;
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ export const panelsReducer = (state = initialState, action) => {
|
|||||||
|
|
||||||
if (forceTopIndex >= 0) {
|
if (forceTopIndex >= 0) {
|
||||||
forceTopPanelsInfo[forceTopIndex] = panel;
|
forceTopPanelsInfo[forceTopIndex] = panel;
|
||||||
} else if (panel.name !== action.payload.name) {
|
} else if (panel.name !== action.payload.name || action.duplicatable) {
|
||||||
newState.push(panel);
|
newState.push(panel);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -67,10 +67,19 @@ export const panelsReducer = (state = initialState, action) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case types.UPDATE_PANEL:
|
case types.UPDATE_PANEL:
|
||||||
|
{
|
||||||
|
let lastIndex = -1;
|
||||||
|
// 배열의 끝에서부터 시작하여 조건에 맞는 마지막 인덱스 찾기
|
||||||
|
for (let i = state.panels.length - 1; i >= 0; i--) {
|
||||||
|
if (state.panels[i].name === action.payload.name) {
|
||||||
|
lastIndex = i;
|
||||||
|
break; // 조건에 맞는 첫 번째 요소를 찾으면 루프 종료
|
||||||
|
}
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
panels: state.panels.map((panel) =>
|
panels: state.panels.map((panel, index) =>
|
||||||
panel.name === action.payload.name
|
index === lastIndex
|
||||||
? {
|
? {
|
||||||
...panel,
|
...panel,
|
||||||
panelInfo: { ...panel.panelInfo, ...action.payload.panelInfo },
|
panelInfo: { ...panel.panelInfo, ...action.payload.panelInfo },
|
||||||
@@ -78,7 +87,7 @@ export const panelsReducer = (state = initialState, action) => {
|
|||||||
: panel
|
: panel
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
}
|
||||||
case types.UPDATE_MODAL_STATUS:
|
case types.UPDATE_MODAL_STATUS:
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
|
|||||||
@@ -13,14 +13,16 @@ import { clearProductDetail } from "../../actions/productActions";
|
|||||||
import TBody from "../../components/TBody/TBody";
|
import TBody from "../../components/TBody/TBody";
|
||||||
import THeader from "../../components/THeader/THeader";
|
import THeader from "../../components/THeader/THeader";
|
||||||
import TPanel from "../../components/TPanel/TPanel";
|
import TPanel from "../../components/TPanel/TPanel";
|
||||||
|
import { panel_names } from "../../utils/Config";
|
||||||
import css from "./DetailPanel.module.less";
|
import css from "./DetailPanel.module.less";
|
||||||
import GroupProduct from "./GroupProduct/GroupProduct";
|
import GroupProduct from "./GroupProduct/GroupProduct";
|
||||||
import SingleProduct from "./SingleProduct/SingleProduct";
|
import SingleProduct from "./SingleProduct/SingleProduct";
|
||||||
import ThemeProduct from "./ThemeProduct/ThemeProduct";
|
import ThemeProduct from "./ThemeProduct/ThemeProduct";
|
||||||
import UnableProduct from "./UnableProduct/UnableProduct";
|
import UnableProduct from "./UnableProduct/UnableProduct";
|
||||||
import YouMayLike from "./YouMayLike/YouMayLike";
|
import YouMayLike from "./YouMayLike/YouMayLike";
|
||||||
|
import { finishVideoPreview } from "../../actions/playActions";
|
||||||
|
|
||||||
export default function DetailPanel() {
|
export default function ItemDetail({panelInfo}) {
|
||||||
const [selectedPatnrId, setSelectedPatnrId] = useState("");
|
const [selectedPatnrId, setSelectedPatnrId] = useState("");
|
||||||
const [selectedPrdtId, setSelectedPrtdId] = useState("");
|
const [selectedPrdtId, setSelectedPrtdId] = useState("");
|
||||||
const [selectedCurationId, setSelectedCurationId] = useState("");
|
const [selectedCurationId, setSelectedCurationId] = useState("");
|
||||||
@@ -94,6 +96,8 @@ export default function DetailPanel() {
|
|||||||
}, [dispatch, selectedPatnrId, selectedPrdtId, panels]);
|
}, [dispatch, selectedPatnrId, selectedPrdtId, panels]);
|
||||||
|
|
||||||
const onClick = useCallback(() => {
|
const onClick = useCallback(() => {
|
||||||
|
// dispatch(resetPanels([{ name: panel_names.DETAIL_PANEL, panelInfo: {} }]));
|
||||||
|
dispatch(finishVideoPreview());
|
||||||
dispatch(popPanel());
|
dispatch(popPanel());
|
||||||
}, [dispatch]);
|
}, [dispatch]);
|
||||||
|
|
||||||
@@ -184,6 +188,7 @@ export default function DetailPanel() {
|
|||||||
selectedPrdtId={selectedPrdtId}
|
selectedPrdtId={selectedPrdtId}
|
||||||
selectedIndex={selectedIndex}
|
selectedIndex={selectedIndex}
|
||||||
setSelectedIndex={setSelectedIndex}
|
setSelectedIndex={setSelectedIndex}
|
||||||
|
launchedFromPlayer={panelInfo.launchedFromPlayer}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{/* 그룹상품 영역 */}
|
{/* 그룹상품 영역 */}
|
||||||
@@ -194,6 +199,7 @@ export default function DetailPanel() {
|
|||||||
selectedPrdtId={selectedPrdtId}
|
selectedPrdtId={selectedPrdtId}
|
||||||
selectedIndex={selectedIndex}
|
selectedIndex={selectedIndex}
|
||||||
setSelectedIndex={setSelectedIndex}
|
setSelectedIndex={setSelectedIndex}
|
||||||
|
launchedFromPlayer={panelInfo.launchedFromPlayer}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{/* 구매불가상품 영역 */}
|
{/* 구매불가상품 영역 */}
|
||||||
@@ -203,6 +209,7 @@ export default function DetailPanel() {
|
|||||||
selectedPrdtId={selectedPrdtId}
|
selectedPrdtId={selectedPrdtId}
|
||||||
selectedIndex={selectedIndex}
|
selectedIndex={selectedIndex}
|
||||||
setSelectedIndex={setSelectedIndex}
|
setSelectedIndex={setSelectedIndex}
|
||||||
|
launchedFromPlayer={panelInfo.launchedFromPlayer}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{/* 테마그룹상품 영역*/}
|
{/* 테마그룹상품 영역*/}
|
||||||
@@ -213,6 +220,7 @@ export default function DetailPanel() {
|
|||||||
selectedCurationId={selectedCurationId}
|
selectedCurationId={selectedCurationId}
|
||||||
selectedPatnrId={selectedPatnrId}
|
selectedPatnrId={selectedPatnrId}
|
||||||
themeType={themeType}
|
themeType={themeType}
|
||||||
|
launchedFromPlayer={panelInfo.launchedFromPlayer}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</TBody>
|
</TBody>
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ export default function UnableProduct({
|
|||||||
selectedPrdtId,
|
selectedPrdtId,
|
||||||
selectedIndex,
|
selectedIndex,
|
||||||
setSelectedIndex,
|
setSelectedIndex,
|
||||||
|
launchedFromPlayer
|
||||||
}) {
|
}) {
|
||||||
const productData = useSelector((state) => state.main.productData);
|
const productData = useSelector((state) => state.main.productData);
|
||||||
|
|
||||||
@@ -46,6 +47,7 @@ export default function UnableProduct({
|
|||||||
productInfo={productData}
|
productInfo={productData}
|
||||||
soldoutFlag={soldout}
|
soldoutFlag={soldout}
|
||||||
promotionCode={promotionCode}
|
promotionCode={promotionCode}
|
||||||
|
launchedFromPlayer={launchedFromPlayer}
|
||||||
/>
|
/>
|
||||||
<IndicatorOptions
|
<IndicatorOptions
|
||||||
productInfo={productData}
|
productInfo={productData}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useCallback, useEffect, useState } from "react";
|
import React, { useCallback, useEffect, useMemo, useState } from "react";
|
||||||
|
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
@@ -15,6 +15,8 @@ import TVirtualGridList from "../../../../components/TVirtualGridList/TVirtualGr
|
|||||||
import useScrollTo from "../../../../hooks/useScrollTo";
|
import useScrollTo from "../../../../hooks/useScrollTo";
|
||||||
import { panel_names } from "../../../../utils/Config";
|
import { panel_names } from "../../../../utils/Config";
|
||||||
import css from "./Indicator.module.less";
|
import css from "./Indicator.module.less";
|
||||||
|
import { finishVideoPreview, startVideoPlayer } from "../../../../actions/playActions";
|
||||||
|
import { scaleW } from "../../../../utils/helperMethods";
|
||||||
|
|
||||||
const Container = SpotlightContainerDecorator(
|
const Container = SpotlightContainerDecorator(
|
||||||
{ enterTo: "last-focused", preserveld: true },
|
{ enterTo: "last-focused", preserveld: true },
|
||||||
@@ -22,47 +24,52 @@ const Container = SpotlightContainerDecorator(
|
|||||||
);
|
);
|
||||||
const SpottableComponent = Spottable("div");
|
const SpottableComponent = Spottable("div");
|
||||||
const SpottableImage = Spottable(Image);
|
const SpottableImage = Spottable(Image);
|
||||||
const IMAGE_WIDTH = 152;
|
const IMAGE_WIDTH = scaleW(152);
|
||||||
export default function Indicator({
|
function Indicator({
|
||||||
selectedIndex,
|
selectedIndex,
|
||||||
setSelectedIndex,
|
setSelectedIndex,
|
||||||
productInfo,
|
productInfo,
|
||||||
soldoutFlag,
|
soldoutFlag,
|
||||||
|
launchedFromPlayer,
|
||||||
promotionCode,
|
promotionCode,
|
||||||
}) {
|
}) {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const [selectedImage, setSelectedImage] = useState(null);
|
|
||||||
const { cursorVisible } = useSelector((state) => state.common.appStatus);
|
const { cursorVisible } = useSelector((state) => state.common.appStatus);
|
||||||
const { getScrollTo, scrollTop } = useScrollTo();
|
const { getScrollTo, scrollTop } = useScrollTo();
|
||||||
|
const [detailVideoPlaying, setDetailVideoPlaying] = useState(!launchedFromPlayer && productInfo.prdtMediaUrl);
|
||||||
let imagePosition = IMAGE_WIDTH * selectedIndex - IMAGE_WIDTH;
|
let imagePosition = IMAGE_WIDTH * selectedIndex - IMAGE_WIDTH;
|
||||||
let images = [];
|
|
||||||
|
|
||||||
console.log("#promotionCode", promotionCode);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (productInfo?.prdtMediaUrl !== null && selectedIndex > 0) {
|
console.log('yhcho productInfo detailVideoPlaying', productInfo, launchedFromPlayer, detailVideoPlaying);
|
||||||
setSelectedImage(images[selectedIndex]);
|
if(detailVideoPlaying && productInfo.prdtMediaUrl){ //auto play
|
||||||
} else {
|
dispatch(startVideoPlayer({
|
||||||
setSelectedImage(productInfo.imgUrls600[selectedIndex]);
|
showUrl: productInfo.prdtMediaUrl,
|
||||||
|
showNm: productInfo.prdtNm,
|
||||||
|
patnrNm: productInfo.patncNm,
|
||||||
|
patncLogoPath: productInfo.patncLogoPath,
|
||||||
|
orderPhnNo: productInfo.orderPhnNo,
|
||||||
|
shptmBanrTpNm: "MEDIA",
|
||||||
|
modal: true,
|
||||||
|
modalContainerId: "indicator_videoContainer", //to calc width, height, left, top
|
||||||
|
modalClassName: selectedIndex === 0 ? css.videoModal:css.videoModalHide
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
}, [selectedIndex, productInfo]);
|
}, [selectedIndex, productInfo, detailVideoPlaying, launchedFromPlayer]);
|
||||||
|
|
||||||
const handleUpClick = useCallback(() => {
|
const handleUpClick = useCallback(() => {
|
||||||
if (selectedIndex === 0) {
|
if (selectedIndex === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setSelectedIndex((prev) => prev - 1);
|
setSelectedIndex((prev) => prev - 1);
|
||||||
if (productInfo.imgUrls600.length - 1 !== selectedIndex) {
|
if (listImages.length - 1 !== selectedIndex) {
|
||||||
scrollTop({ y: imagePosition - IMAGE_WIDTH, animate: true });
|
scrollTop({ y: imagePosition - IMAGE_WIDTH, animate: true });
|
||||||
}
|
}
|
||||||
}, [selectedIndex]);
|
}, [selectedIndex, listImages]);
|
||||||
|
|
||||||
const handleDownClick = useCallback(() => {
|
const handleDownClick = useCallback(() => {
|
||||||
if (productInfo.imgUrls600.length - 1 === selectedIndex) {
|
if (listImages.length.length - 1 === selectedIndex) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setSelectedIndex((prev) => prev + 1);
|
setSelectedIndex((prev) => prev + 1);
|
||||||
if (selectedIndex > 1) {
|
if (selectedIndex > 1) {
|
||||||
scrollTop({
|
scrollTop({
|
||||||
@@ -70,23 +77,30 @@ export default function Indicator({
|
|||||||
animate: true,
|
animate: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [selectedIndex, selectedImage]);
|
}, [selectedIndex, listImages]);
|
||||||
|
|
||||||
|
const canPlayVideo = useMemo(()=>{
|
||||||
|
return productInfo.prdtMediaUrl && selectedIndex === 0;
|
||||||
|
},[productInfo, selectedIndex]);
|
||||||
|
|
||||||
const handleVideoOnClick = useCallback(() => {
|
const handleVideoOnClick = useCallback(() => {
|
||||||
dispatch(
|
if(canPlayVideo){
|
||||||
pushPanel({
|
dispatch(startVideoPlayer({
|
||||||
name: panel_names.PLAYER_PANEL,
|
showUrl: productInfo.prdtMediaUrl,
|
||||||
panelInfo: {
|
showNm: productInfo.prdtNm,
|
||||||
showUrl: productInfo.prdtMediaUrl,
|
patnrNm: productInfo.patncNm,
|
||||||
showNm: productInfo.prdtNm,
|
patncLogoPath: productInfo.patncLogoPath,
|
||||||
patnrNm: productInfo.patncNm,
|
orderPhnNo: productInfo.orderPhnNo,
|
||||||
patncLogoPath: productInfo.patncLogoPath,
|
shptmBanrTpNm: "MEDIA",
|
||||||
orderPhnNo: productInfo.orderPhnNo,
|
modal: !detailVideoPlaying,
|
||||||
shptmBanrTpNm: "MEDIA",
|
modalContainerId: "indicator_videoContainer", //to calc width, height, left, top
|
||||||
},
|
modalClassName:css.videoModal
|
||||||
})
|
}));
|
||||||
);
|
if(!detailVideoPlaying){
|
||||||
}, [dispatch]);
|
setDetailVideoPlaying(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [dispatch, productInfo, detailVideoPlaying, canPlayVideo]);
|
||||||
|
|
||||||
const onSpotlightRight = useCallback(() => {
|
const onSpotlightRight = useCallback(() => {
|
||||||
const timer = setTimeout(() => {
|
const timer = setTimeout(() => {
|
||||||
@@ -104,18 +118,27 @@ export default function Indicator({
|
|||||||
const onSpotlightLeft = useCallback(() => {
|
const onSpotlightLeft = useCallback(() => {
|
||||||
Spotlight.focus("spotlightId_backBtn");
|
Spotlight.focus("spotlightId_backBtn");
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const listImages = useMemo(()=>{
|
||||||
|
const images = [...productInfo.imgUrls600];
|
||||||
|
if (productInfo?.prdtMediaUrl !== null) {
|
||||||
|
images.splice(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
productInfo.thumbnailUrl960 !== null
|
||||||
|
? productInfo.thumbnailUrl960
|
||||||
|
: defaultImage
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return images;
|
||||||
|
},[productInfo]);
|
||||||
|
|
||||||
|
const selectedImage = useMemo(()=>{
|
||||||
|
return listImages[selectedIndex];
|
||||||
|
},[listImages, selectedIndex]);
|
||||||
|
|
||||||
const renderItem = useCallback(
|
const renderItem = useCallback(
|
||||||
({ index, ...rest }) => {
|
({ index, ...rest }) => {
|
||||||
images = [...productInfo.imgUrls600];
|
|
||||||
if (productInfo?.prdtMediaUrl !== null) {
|
|
||||||
images.splice(
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
productInfo.thumbnailUrl960 !== null
|
|
||||||
? productInfo.thumbnailUrl960
|
|
||||||
: defaultImage
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleItemClick = () => {
|
const handleItemClick = () => {
|
||||||
setSelectedIndex(index);
|
setSelectedIndex(index);
|
||||||
@@ -124,7 +147,7 @@ export default function Indicator({
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<SpottableImage
|
<SpottableImage
|
||||||
src={images[index]}
|
src={listImages[index]}
|
||||||
alt=""
|
alt=""
|
||||||
className={classNames(
|
className={classNames(
|
||||||
css.image,
|
css.image,
|
||||||
@@ -143,63 +166,34 @@ export default function Indicator({
|
|||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
[productInfo, selectedIndex, selectedImage]
|
[listImages, selectedIndex]
|
||||||
);
|
);
|
||||||
|
|
||||||
const renderThumbnail = useCallback(() => {
|
const renderThumbnail = useCallback(() => {
|
||||||
return (
|
return (
|
||||||
<div className={css.thumbnailContainer}>
|
<div className={css.thumbnailContainer}>
|
||||||
{productInfo && productInfo.prdtMediaUrl !== null ? (
|
<SpottableComponent
|
||||||
selectedIndex === 0 ? (
|
spotlightId="indicator_videoContainer"
|
||||||
<>
|
spotlightDisabled={!canPlayVideo}
|
||||||
<SpottableComponent
|
disabled={!canPlayVideo}
|
||||||
className={css.player}
|
className={classNames(css.thumbnail, soldoutFlag && css.soldout)}
|
||||||
onClick={handleVideoOnClick}
|
onSpotlightRight={onSpotlightRight}
|
||||||
onSpotlightUp={onSpotlightUp}
|
onSpotlightLeft={onSpotlightLeft}
|
||||||
onSpotlightLeft={onSpotlightLeft}
|
onClick={handleVideoOnClick}
|
||||||
spotlightId="spotlight_videoThumbnail"
|
>
|
||||||
>
|
<>
|
||||||
<TVideoPlayer
|
<Image
|
||||||
showUrl={productInfo.prdtMediaUrl}
|
className={css.image}
|
||||||
width={560}
|
src={detailVideoPlaying && canPlayVideo ? "" : selectedImage}
|
||||||
height={560}
|
|
||||||
videoIsPlaying
|
|
||||||
/>
|
|
||||||
</SpottableComponent>
|
|
||||||
{productInfo && productInfo[selectedIndex]?.disclaimer && (
|
|
||||||
<div className={css.disclaimerContainer}>
|
|
||||||
<span className={css.icon} />
|
|
||||||
<div className={css.disclaimer}>
|
|
||||||
{productInfo?.disclaimer}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<SpottableImage
|
|
||||||
src={selectedImage}
|
|
||||||
alt=""
|
|
||||||
className={classNames(css.thumbnail, soldoutFlag && css.soldout)}
|
|
||||||
onSpotlightRight={onSpotlightRight}
|
|
||||||
onSpotlightLeft={onSpotlightLeft}
|
|
||||||
>
|
|
||||||
{soldoutFlag && <h3 className={css.soldoutLabel}>SOLD OUT</h3>}
|
|
||||||
</SpottableImage>
|
|
||||||
)
|
|
||||||
) : (
|
|
||||||
<SpottableImage
|
|
||||||
src={selectedImage}
|
|
||||||
alt=""
|
alt=""
|
||||||
className={classNames(css.thumbnail, soldoutFlag && css.soldout)}
|
></Image>
|
||||||
onSpotlightRight={onSpotlightRight}
|
{soldoutFlag && <h3 className={css.soldoutLabel}>SOLD OUT</h3>}
|
||||||
onSpotlightLeft={onSpotlightLeft}
|
{!detailVideoPlaying && canPlayVideo && <h3 className={css.soldoutLabel}>Play Button (todo)</h3>}
|
||||||
>
|
</>
|
||||||
{soldoutFlag && <h3 className={css.soldoutLabel}>SOLD OUT</h3>}
|
</SpottableComponent>
|
||||||
</SpottableImage>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}, [productInfo, selectedIndex, selectedImage, soldoutFlag]);
|
}, [productInfo, selectedIndex, selectedImage, soldoutFlag, detailVideoPlaying]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container className={css.indicatorContainer}>
|
<Container className={css.indicatorContainer}>
|
||||||
@@ -218,10 +212,10 @@ export default function Indicator({
|
|||||||
<TVirtualGridList
|
<TVirtualGridList
|
||||||
cbScrollTo={getScrollTo}
|
cbScrollTo={getScrollTo}
|
||||||
className={css.tVirtualGridList}
|
className={css.tVirtualGridList}
|
||||||
dataSize={productInfo.imgUrls600.length}
|
dataSize={listImages.length}
|
||||||
itemWidth={144}
|
itemWidth={scaleW(144)}
|
||||||
itemHeight={144}
|
itemHeight={scaleW(144)}
|
||||||
spacing={8}
|
spacing={scaleW(8)}
|
||||||
renderItem={renderItem}
|
renderItem={renderItem}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@@ -231,10 +225,27 @@ export default function Indicator({
|
|||||||
spotlightDisabled={!cursorVisible}
|
spotlightDisabled={!cursorVisible}
|
||||||
className={classNames(
|
className={classNames(
|
||||||
css.downButton,
|
css.downButton,
|
||||||
productInfo.imgUrls600.length - 1 === selectedIndex && css.disable
|
(listImages.length-1) === selectedIndex && css.disable
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
const propsAreEqual = (prev, next) => {
|
||||||
|
const keys = Object.keys(prev);
|
||||||
|
const nextKeys = Object.keys(next);
|
||||||
|
if(keys.length !== nextKeys.length){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for(let i=0; i<keys.length; i++){
|
||||||
|
if(prev[keys[i]] !== next[keys[i]]){
|
||||||
|
if (JSON.stringify(prev[keys[i]]) === JSON.stringify(next[keys[i]])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
export default React.memo(Indicator, propsAreEqual);
|
||||||
@@ -76,7 +76,11 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
.image{
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
> h3 {
|
> h3 {
|
||||||
font-size: 36px;
|
font-size: 36px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
@@ -231,3 +235,9 @@
|
|||||||
|
|
||||||
//isOptions
|
//isOptions
|
||||||
}
|
}
|
||||||
|
.videoModal::after {
|
||||||
|
// .focused(@boxShadow:22px, @borderRadius: 0px);
|
||||||
|
}
|
||||||
|
.videoModalHide {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import React, { useCallback, useEffect, useState } from "react";
|
|||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import { useDispatch } from "react-redux";
|
import { useDispatch } from "react-redux";
|
||||||
|
|
||||||
import Spotlight from "@enact/spotlight";
|
|
||||||
import SpotlightContainerDecorator from "@enact/spotlight/SpotlightContainerDecorator";
|
import SpotlightContainerDecorator from "@enact/spotlight/SpotlightContainerDecorator";
|
||||||
import Spottable from "@enact/spotlight/Spottable";
|
import Spottable from "@enact/spotlight/Spottable";
|
||||||
|
|
||||||
@@ -14,11 +13,11 @@ import emptyVerImage from "../../../../assets/images/img-home-banner-empty-ver.p
|
|||||||
import liveShow from "../../../../assets/images/tag-liveshow.png";
|
import liveShow from "../../../../assets/images/tag-liveshow.png";
|
||||||
import { pushPanel } from "../../../actions/panelActions";
|
import { pushPanel } from "../../../actions/panelActions";
|
||||||
import CustomImage from "../../../components/CustomImage/CustomImage";
|
import CustomImage from "../../../components/CustomImage/CustomImage";
|
||||||
import TVideoPlayer from "../../../components/TVideoPlayer/TVideoPlayer";
|
|
||||||
import usePriceInfo from "../../../hooks/usePriceInfo";
|
import usePriceInfo from "../../../hooks/usePriceInfo";
|
||||||
import useScrollReset from "../../../hooks/useScrollReset";
|
import useScrollReset from "../../../hooks/useScrollReset";
|
||||||
import { panel_names } from "../../../utils/Config";
|
import { panel_names } from "../../../utils/Config";
|
||||||
import css from "./RandomUnit.module.less";
|
import css from "./RandomUnit.module.less";
|
||||||
|
import { finishVideoPreview, startVideoPlayer } from "../../../actions/playActions";
|
||||||
|
|
||||||
const SpottableComponent = Spottable("div");
|
const SpottableComponent = Spottable("div");
|
||||||
|
|
||||||
@@ -35,13 +34,11 @@ export default function RandomUnit({
|
|||||||
}) {
|
}) {
|
||||||
const bannerDetailInfos = bannerData.bannerDetailInfos;
|
const bannerDetailInfos = bannerData.bannerDetailInfos;
|
||||||
|
|
||||||
const [videoIsPlaying, setVideoIsPlaying] = useState(false);
|
|
||||||
const [randomData, setRandomData] = useState("");
|
const [randomData, setRandomData] = useState("");
|
||||||
const [priceInfos, setpriceInfos] = useState("");
|
const [priceInfos, setpriceInfos] = useState("");
|
||||||
const [videoIndex, setVideoIndex] = useState([]);
|
const [videoIndex, setVideoIndex] = useState([]);
|
||||||
const [videoError, setVideoError] = useState(false);
|
const [videoError, setVideoError] = useState(false);
|
||||||
const [randomNumber, setRandomNumber] = useState("");
|
const [randomNumber, setRandomNumber] = useState("");
|
||||||
|
|
||||||
const { handleScrollReset, handleStopScrolling } =
|
const { handleScrollReset, handleStopScrolling } =
|
||||||
useScrollReset(scrollTopBody);
|
useScrollReset(scrollTopBody);
|
||||||
|
|
||||||
@@ -86,14 +83,28 @@ export default function RandomUnit({
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 포커스 인
|
// 포커스 인
|
||||||
const onFocus = () => {
|
const onFocus = (ev) => {
|
||||||
setVideoIsPlaying(true);
|
|
||||||
handleScrollReset();
|
handleScrollReset();
|
||||||
|
if(randomData.shptmBanrTpNm == "LIVE" ||
|
||||||
|
randomData.shptmBanrTpNm == "VOD"){
|
||||||
|
dispatch(startVideoPlayer({
|
||||||
|
patnrId: randomData.patnrId,
|
||||||
|
showId: randomData.showId,
|
||||||
|
shptmBanrTpNm: randomData.shptmBanrTpNm,
|
||||||
|
lgCatCd: randomData.lgCatCd,
|
||||||
|
modal: true,
|
||||||
|
modalContainerId: spotlightId, //to calc width, height, left, top
|
||||||
|
modalClassName:css.videoModal
|
||||||
|
}));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 포커스 아웃
|
// 포커스 아웃
|
||||||
const onBlur = () => {
|
const onBlur = (ev) => {
|
||||||
setVideoIsPlaying(false);
|
if(randomData.shptmBanrTpNm == "LIVE" ||
|
||||||
|
randomData.shptmBanrTpNm == "VOD"){
|
||||||
|
dispatch(finishVideoPreview());
|
||||||
|
}
|
||||||
handleStopScrolling();
|
handleStopScrolling();
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -119,17 +130,13 @@ export default function RandomUnit({
|
|||||||
} else if (randomData.shptmLnkTpNm === "Product Detail") {
|
} else if (randomData.shptmLnkTpNm === "Product Detail") {
|
||||||
panelName = panel_names.DETAIL_PANEL;
|
panelName = panel_names.DETAIL_PANEL;
|
||||||
} else if (randomData.shptmLnkTpNm === "VOD") {
|
} else if (randomData.shptmLnkTpNm === "VOD") {
|
||||||
return dispatch(
|
return dispatch(startVideoPlayer({
|
||||||
pushPanel({
|
|
||||||
name: panel_names.PLAYER_PANEL,
|
|
||||||
panelInfo: {
|
|
||||||
patnrId: randomData.patnrId,
|
patnrId: randomData.patnrId,
|
||||||
showId: randomData.showId,
|
showId: randomData.showId,
|
||||||
shptmBanrTpNm: "VOD",
|
shptmBanrTpNm: "VOD",
|
||||||
lgCatCd: randomData.lgCatCd,
|
lgCatCd: randomData.lgCatCd,
|
||||||
},
|
modal: false
|
||||||
})
|
}));
|
||||||
);
|
|
||||||
} else if (randomData.shptmLnkTpNm === "Show Detail") {
|
} else if (randomData.shptmLnkTpNm === "Show Detail") {
|
||||||
panelName = panel_names.HOME_PANEL;
|
panelName = panel_names.HOME_PANEL;
|
||||||
} else if (randomData.shptmLnkTpNm === "Theme Page") {
|
} else if (randomData.shptmLnkTpNm === "Theme Page") {
|
||||||
@@ -166,19 +173,17 @@ export default function RandomUnit({
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 비디오 클릭
|
// 비디오 클릭
|
||||||
const videoClick = () => {
|
const videoClick = useCallback(() => {
|
||||||
dispatch(
|
dispatch(startVideoPlayer({
|
||||||
pushPanel({
|
patnrId: randomData.patnrId,
|
||||||
name: panel_names.PLAYER_PANEL,
|
|
||||||
panelInfo: {
|
|
||||||
patnrId: randomData.patnrId,
|
|
||||||
showId: randomData.showId,
|
showId: randomData.showId,
|
||||||
shptmBanrTpNm: randomData.shptmBanrTpNm,
|
shptmBanrTpNm: randomData.shptmBanrTpNm,
|
||||||
lgCatCd: randomData.lgCatCd,
|
lgCatCd: randomData.lgCatCd,
|
||||||
},
|
modal: false,
|
||||||
})
|
modalContainerId: spotlightId, //to calc width, height, left, top
|
||||||
);
|
modalClassName:css.videoModal
|
||||||
};
|
}));
|
||||||
|
},[randomData, spotlightId]);
|
||||||
|
|
||||||
// 투데이즈 딜 가격 정보
|
// 투데이즈 딜 가격 정보
|
||||||
const { originalPrice, discountedPrice, discountRate, offerInfo } =
|
const { originalPrice, discountedPrice, discountRate, offerInfo } =
|
||||||
@@ -260,72 +265,40 @@ export default function RandomUnit({
|
|||||||
</p>
|
</p>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
{/* 비디오 오류, 비디오 포커스 상태, 라이브 일 경우 */}
|
{/* 비디오 오류, --> Player 내부로 이동 */}
|
||||||
{videoError === true &&
|
<div
|
||||||
videoIsPlaying === true &&
|
className={classNames(
|
||||||
randomData.shptmBanrTpNm === "LIVE" ? (
|
css.itemBox,
|
||||||
<div className={css.errorContents}>
|
isHorizontal && css.isHorizontal
|
||||||
<div>
|
)}
|
||||||
<CustomImage
|
>
|
||||||
delay={0}
|
{/* {randomData.tmnlImgPath !== null ? (
|
||||||
className={css.errorlogo}
|
<img src={randomData.tmnlImgPath} />
|
||||||
src={randomData.patncLogoPath}
|
) : null} */}
|
||||||
animationSpeed="fast"
|
{randomData.tmnlImgPath == null ? (
|
||||||
/>
|
|
||||||
<p className={css.errorText}>
|
|
||||||
Click the screen to see more products!
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
|
|
||||||
{videoIsPlaying === false ? (
|
|
||||||
<div
|
|
||||||
className={classNames(
|
|
||||||
css.itemBox,
|
|
||||||
isHorizontal && css.isHorizontal
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{/* {randomData.tmnlImgPath !== null ? (
|
|
||||||
<img src={randomData.tmnlImgPath} />
|
|
||||||
) : null} */}
|
|
||||||
{randomData.tmnlImgPath == null ? (
|
|
||||||
<CustomImage
|
|
||||||
delay={0}
|
|
||||||
src={
|
|
||||||
randomData.vtctpYn === "Y" ? emptyVerImage : emptyHorImage
|
|
||||||
}
|
|
||||||
animationSpeed="fast"
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<CustomImage
|
|
||||||
delay={0}
|
|
||||||
src={randomData.tmnlImgPath}
|
|
||||||
animationSpeed="fast"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<TVideoPlayer
|
|
||||||
showUrl={randomData.showUrl}
|
|
||||||
className={""}
|
|
||||||
videoIsPlaying={videoIsPlaying}
|
|
||||||
width={randomData.vtctpYn === "Y" ? 486 : 744}
|
|
||||||
height={randomData.vtctpYn === "Y" ? 858 : 420}
|
|
||||||
videoErrorCheck={videoErrorCheck}
|
|
||||||
videoType={randomData.shptmBanrTpNm}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{videoError === false ? (
|
|
||||||
<p className={css.brandIcon}>
|
|
||||||
<CustomImage
|
<CustomImage
|
||||||
delay={0}
|
delay={0}
|
||||||
src={randomData.patncLogoPath}
|
src={
|
||||||
|
randomData.vtctpYn === "Y" ? emptyVerImage : emptyHorImage
|
||||||
|
}
|
||||||
animationSpeed="fast"
|
animationSpeed="fast"
|
||||||
/>
|
/>
|
||||||
</p>
|
) : (
|
||||||
) : null}
|
<CustomImage
|
||||||
|
delay={0}
|
||||||
|
src={randomData.tmnlImgPath}
|
||||||
|
animationSpeed="fast"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<p className={css.brandIcon}>
|
||||||
|
<CustomImage
|
||||||
|
delay={0}
|
||||||
|
src={randomData.patncLogoPath}
|
||||||
|
animationSpeed="fast"
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
|
|
||||||
</SpottableComponent>
|
</SpottableComponent>
|
||||||
) : randomData.shptmBanrTpNm == "Today's Deals" ? (
|
) : randomData.shptmBanrTpNm == "Today's Deals" ? (
|
||||||
<SpottableComponent
|
<SpottableComponent
|
||||||
|
|||||||
@@ -185,3 +185,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.videoModal::after {
|
||||||
|
.focused(@boxShadow:22px, @borderRadius: 12px);
|
||||||
|
}
|
||||||
@@ -69,8 +69,8 @@ export default function MainView() {
|
|||||||
const [tabActivated, setTabActivated] = useState(false);
|
const [tabActivated, setTabActivated] = useState(false);
|
||||||
|
|
||||||
const isOnTop = useMemo(() => {
|
const isOnTop = useMemo(() => {
|
||||||
return !mainIndex && panels.length <= 0;
|
return !mainIndex && (panels.length <= 0 || (panels.length===1 && panels[0].panelInfo.modal));
|
||||||
}, [mainIndex, panels.length]);
|
}, [mainIndex, panels]);
|
||||||
|
|
||||||
const onPreImageLoadComplete = useCallback(() => {
|
const onPreImageLoadComplete = useCallback(() => {
|
||||||
console.log("MainView onPreImageLoadComplete");
|
console.log("MainView onPreImageLoadComplete");
|
||||||
@@ -79,20 +79,49 @@ export default function MainView() {
|
|||||||
|
|
||||||
const renderTopPanel = useCallback(() => {
|
const renderTopPanel = useCallback(() => {
|
||||||
if (panels && panels.length > 0) {
|
if (panels && panels.length > 0) {
|
||||||
|
let renderringPanels = [];
|
||||||
|
if(panels[panels.length-1]?.name === Config.panel_names.PLAYER_PANEL ||
|
||||||
|
panels[panels.length-2]?.name === Config.panel_names.PLAYER_PANEL
|
||||||
|
){
|
||||||
|
renderringPanels = panels.slice(-2);
|
||||||
|
}else{
|
||||||
|
renderringPanels = panels.slice(-1);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{isOnTop &&
|
||||||
|
<HomePanel isOnTop={isOnTop} />
|
||||||
|
}
|
||||||
|
{renderringPanels.map((panel, index) => {
|
||||||
|
const Component = panelMap[panel.name];
|
||||||
|
//render last two panels if there's videoplayer
|
||||||
|
return <Component
|
||||||
|
key={panel.name+(panels.indexOf(panel))}//to maintain react virtual dom
|
||||||
|
panelInfo={panel.panelInfo}
|
||||||
|
spotlightId={panel.name}
|
||||||
|
isTabActivated={tabActivated}
|
||||||
|
isOnTop={index===(renderringPanels.length-1)}
|
||||||
|
/>
|
||||||
|
})}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
/*
|
||||||
const panel = panels[panels.length - 1];
|
const panel = panels[panels.length - 1];
|
||||||
const Component = panelMap[panel.name];
|
const Component = panelMap[panel.name];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Component
|
<Component
|
||||||
panelInfo={panel.panelInfo}
|
panelInfo={panel.panelInfo}
|
||||||
spotlightId={panel.name}
|
spotlightId={panel.name}
|
||||||
isTabActivated={tabActivated}
|
isTabActivated={tabActivated}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
} else {
|
*/
|
||||||
return null;
|
} else if(isOnTop){
|
||||||
|
return <HomePanel isOnTop={isOnTop} />;
|
||||||
}
|
}
|
||||||
}, [panels]);
|
return null;
|
||||||
|
}, [panels, tabActivated, isOnTop]);
|
||||||
|
|
||||||
const onTabActivated = useCallback((activated) => {
|
const onTabActivated = useCallback((activated) => {
|
||||||
setTabActivated(activated);
|
setTabActivated(activated);
|
||||||
@@ -100,7 +129,12 @@ export default function MainView() {
|
|||||||
|
|
||||||
const topPanelName = useMemo(() => {
|
const topPanelName = useMemo(() => {
|
||||||
if (panels && panels.length > 0) {
|
if (panels && panels.length > 0) {
|
||||||
return panels[panels.length - 1].name;
|
let targetName = panels[panels.length - 1].name;
|
||||||
|
if(panels[panels.length - 1].name === Config.panel_names.PLAYER_PANEL
|
||||||
|
&& panels[panels.length - 1].panelInfo.modal){
|
||||||
|
targetName = panels[panels.length - 2]?.name;
|
||||||
|
}
|
||||||
|
return targetName;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
@@ -108,9 +142,14 @@ export default function MainView() {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (panels && panels.length > 0) {
|
if (panels && panels.length > 0) {
|
||||||
const panel = panels[panels.length - 1];
|
let panel = panels[panels.length - 1];
|
||||||
|
if(panels[panels.length - 1].name === Config.panel_names.PLAYER_PANEL
|
||||||
Spotlight.focus(panel.name);
|
&& panels[panels.length - 1].panelInfo.modal){
|
||||||
|
panel = panels[panels.length - 2];
|
||||||
|
}
|
||||||
|
if(panel?.name){
|
||||||
|
Spotlight.focus(panel?.name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, [panels]);
|
}, [panels]);
|
||||||
|
|
||||||
@@ -155,11 +194,9 @@ export default function MainView() {
|
|||||||
showLoadingPanel.type === "launching" ? css.transparent : null
|
showLoadingPanel.type === "launching" ? css.transparent : null
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{isOnTop ? (
|
{
|
||||||
<HomePanel isOnTop={isOnTop} />
|
|
||||||
) : (
|
|
||||||
<div className={classNames(css.mainlayout)}>{renderTopPanel()}</div>
|
<div className={classNames(css.mainlayout)}>{renderTopPanel()}</div>
|
||||||
)}
|
}
|
||||||
<TabLayout
|
<TabLayout
|
||||||
topPanelName={topPanelName}
|
topPanelName={topPanelName}
|
||||||
onTabActivated={onTabActivated}
|
onTabActivated={onTabActivated}
|
||||||
|
|||||||
@@ -7,8 +7,8 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row-reverse;
|
flex-direction: row-reverse;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
width: 100%;
|
width: 100vw;
|
||||||
height: 100%;
|
height: 100vh;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,9 +16,9 @@ export default function PlayerOverlayHeader({
|
|||||||
onClick,
|
onClick,
|
||||||
setSelectedIndex,
|
setSelectedIndex,
|
||||||
}) {
|
}) {
|
||||||
const onClickBack = () => {
|
const onClickBack = (ev) => {
|
||||||
if (onClick) {
|
if (onClick) {
|
||||||
onClick();
|
onClick(ev);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ import {
|
|||||||
} from "../../actions/mainActions";
|
} from "../../actions/mainActions";
|
||||||
import * as PanelActions from "../../actions/panelActions";
|
import * as PanelActions from "../../actions/panelActions";
|
||||||
import { updatePanel } from "../../actions/panelActions";
|
import { updatePanel } from "../../actions/panelActions";
|
||||||
import { getChatLog, getSubTitle } from "../../actions/playActions";
|
import { getChatLog, getSubTitle, startVideoPlayer } from "../../actions/playActions";
|
||||||
import { MediaControls } from "../../components/MediaPlayer";
|
import { MediaControls } from "../../components/MediaPlayer";
|
||||||
import TButton from "../../components/TButton/TButton";
|
import TButton from "../../components/TButton/TButton";
|
||||||
import TButtonTab, { LIST_TYPE } from "../../components/TButtonTab/TButtonTab";
|
import TButtonTab, { LIST_TYPE } from "../../components/TButtonTab/TButtonTab";
|
||||||
@@ -53,12 +53,12 @@ const unableToPlay = new Job((callback) => {
|
|||||||
// callback();
|
// callback();
|
||||||
}, 5000);
|
}, 5000);
|
||||||
|
|
||||||
const PlayerPanel = ({ hideChildren, isTabActivated, ...props }) => {
|
const PlayerPanel = ({ hideChildren, isTabActivated, panelInfo, isOnTop, ...props }) => {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const videoPlayer = useRef(null);
|
const videoPlayer = useRef(null);
|
||||||
const [playListInfo, setPlayListInfo] = useState("");
|
const [playListInfo, setPlayListInfo] = useState("");
|
||||||
const [shopNowInfo, setShopNowInfo] = useState();
|
const [shopNowInfo, setShopNowInfo] = useState();
|
||||||
const [panelInfo, setPaneInfo] = useState([]);
|
const [modalStyle, setModalStyle] = useState({});
|
||||||
const [tab, setTab] = useState(0);
|
const [tab, setTab] = useState(0);
|
||||||
const [sideOpen, setSideOpen] = useState(true);
|
const [sideOpen, setSideOpen] = useState(true);
|
||||||
const [selectedIndex, setSelectedIndex] = useState(0);
|
const [selectedIndex, setSelectedIndex] = useState(0);
|
||||||
@@ -81,22 +81,27 @@ const PlayerPanel = ({ hideChildren, isTabActivated, ...props }) => {
|
|||||||
$L(panelInfo?.shptmBanrTpNm === "LIVE" ? "LIVE CHANNEL" : "FEATURED SHOWS"),
|
$L(panelInfo?.shptmBanrTpNm === "LIVE" ? "LIVE CHANNEL" : "FEATURED SHOWS"),
|
||||||
];
|
];
|
||||||
|
|
||||||
// 패널 정보 받기
|
|
||||||
const getPanelInfo = useCallback(() => {
|
|
||||||
if (panels) {
|
|
||||||
for (let i = 0; i < panels.length; i++) {
|
|
||||||
if (panels[i].name === "playerpanel") {
|
|
||||||
setPaneInfo(panels[i].panelInfo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [panels]);
|
|
||||||
|
|
||||||
const onClickBack = useCallback(
|
const onClickBack = useCallback(
|
||||||
(ev) => {
|
(ev) => {
|
||||||
dispatch(PanelActions.popPanel());
|
console.log('yhcho onClickBack', panelInfo);
|
||||||
|
//modal로부터 Full 전환된 경우 다시 preview 모드로 돌아감.
|
||||||
|
if(panelInfo.modalContainerId && !panelInfo.modal){
|
||||||
|
dispatch(startVideoPlayer({
|
||||||
|
...panelInfo,
|
||||||
|
modal: true,
|
||||||
|
}));
|
||||||
|
videoPlayer.current?.hideControls();
|
||||||
|
ev.stopPropagation();
|
||||||
|
ev.preventDefault();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(!panelInfo.modal){
|
||||||
|
dispatch(PanelActions.popPanel());
|
||||||
|
ev.stopPropagation();
|
||||||
|
ev.preventDefault();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
[dispatch]
|
[dispatch, panelInfo]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleItemClick = useCallback(
|
const handleItemClick = useCallback(
|
||||||
@@ -127,11 +132,6 @@ const PlayerPanel = ({ hideChildren, isTabActivated, ...props }) => {
|
|||||||
];
|
];
|
||||||
setPlayListInfo(updatedPlayListInfo);
|
setPlayListInfo(updatedPlayListInfo);
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
getPanelInfo();
|
|
||||||
}, [panels, panelInfo, dispatch]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (panelInfo) {
|
if (panelInfo) {
|
||||||
dispatch(
|
dispatch(
|
||||||
@@ -160,7 +160,7 @@ const PlayerPanel = ({ hideChildren, isTabActivated, ...props }) => {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}, [dispatch, panelInfo, selectedIndex, panels]);
|
}, [dispatch, panelInfo, selectedIndex]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (panelInfo?.shptmBanrTpNm === "LIVE") {
|
if (panelInfo?.shptmBanrTpNm === "LIVE") {
|
||||||
@@ -172,7 +172,7 @@ const PlayerPanel = ({ hideChildren, isTabActivated, ...props }) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [panelInfo, playListInfo]);
|
}, [panelInfo?.shptmBanrTpNm, playListInfo]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (
|
if (
|
||||||
@@ -246,45 +246,77 @@ const PlayerPanel = ({ hideChildren, isTabActivated, ...props }) => {
|
|||||||
}, [isActive]);
|
}, [isActive]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (panelInfo && shopNowInfo) {
|
if(!panelInfo.modal){
|
||||||
Spotlight.focus("playVideoShopNowBox");
|
if (panelInfo && shopNowInfo) {
|
||||||
Spotlight.focus(`spotlightId-video-${panelInfo.index}`);
|
Spotlight.focus("playVideoShopNowBox");
|
||||||
return;
|
Spotlight.focus(`spotlightId-video-${panelInfo.index}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Spotlight.focus("hotpicks-close-arrow");
|
||||||
}
|
}
|
||||||
Spotlight.focus("hotpicks-close-arrow");
|
|
||||||
}, [panelInfo, shopNowInfo, playListInfo]);
|
}, [panelInfo, shopNowInfo, playListInfo]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if(panelInfo.modal && panelInfo.modalContainerId){
|
||||||
|
const node = document.querySelector(`[data-spotlight-id="${panelInfo.modalContainerId}"]`);
|
||||||
|
if(node){
|
||||||
|
const {width, height, top, left} = node.getBoundingClientRect();
|
||||||
|
const modalStyle = {width: width+'px', height: height+'px', top: top+'px', left: left+'px', position: 'fixed'};
|
||||||
|
setModalStyle(modalStyle);
|
||||||
|
dispatch(updatePanel({name: panel_names.PLAYER_PANEL, panelInfo: {modalStyle: modalStyle}}));
|
||||||
|
}else{
|
||||||
|
setModalStyle(panelInfo.modalStyle);
|
||||||
|
console.error('PlayerPanel modalContainerId node not found', panelInfo.modalContainerId);
|
||||||
|
}
|
||||||
|
}else if(isOnTop && !panelInfo.modal && videoPlayer.current?.getMediaState()?.paused){
|
||||||
|
videoPlayer.current.play();
|
||||||
|
}
|
||||||
|
}, [panelInfo, isOnTop]);
|
||||||
|
|
||||||
|
const cannotPlay = useMemo(()=>{
|
||||||
|
const topPanel = panels[panels.length-1];
|
||||||
|
return !isOnTop && topPanel?.name === panel_names.PLAYER_PANEL;
|
||||||
|
},[panels, isOnTop]);
|
||||||
|
|
||||||
delete props.panelInfo;
|
delete props.panelInfo;
|
||||||
|
|
||||||
|
|
||||||
|
const getPlayer = useCallback((ref) => {
|
||||||
|
videoPlayer.current = ref;
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TPanel isTabActivated={false} {...props} className={css.videoContainer}>
|
<TPanel isTabActivated={false} {...props} className={classNames(css.videoContainer, panelInfo.modal && css.modal, !isOnTop && css.background)} handleCancel={onClickBack}>
|
||||||
<VideoPlayer
|
<VideoPlayer
|
||||||
// setApiProvider={getPlayer}
|
setApiProvider={getPlayer}
|
||||||
onEnded={onClickBack} // 플레이어가 끝날때 호출
|
disabled={panelInfo.modal}
|
||||||
noAutoPlay={false} // 오토플레이
|
onEnded={onClickBack} // 플레이어가 끝날때 호출
|
||||||
autoCloseTimeout={100000000000000} // 컨트롤 버튼 숨기는 시간
|
noAutoPlay={cannotPlay} // 오토플레이
|
||||||
playListInfo={playListInfo && playListInfo[selectedIndex]} // 비디오 데이터
|
autoCloseTimeout={100000000000000} // 컨트롤 버튼 숨기는 시간
|
||||||
onBackButton={onClickBack} // 뒤로가기 버튼
|
playListInfo={playListInfo && playListInfo[selectedIndex]} // 비디오 데이터
|
||||||
panelInfo={panelInfo} // 패널정보 (라이브,VOD)
|
onBackButton={onClickBack} // 뒤로가기 버튼
|
||||||
setSelectedIndex={setSelectedIndex} // 선택한 인덱스 Set
|
panelInfo={panelInfo} // 패널정보 (라이브,VOD)
|
||||||
selectedIndex={selectedIndex} // 선택한 인덱스
|
setSelectedIndex={setSelectedIndex} // 선택한 인덱스 Set
|
||||||
spotlightDisabled={sideOpen}
|
selectedIndex={selectedIndex} // 선택한 인덱스
|
||||||
src={playListInfo && playListInfo[selectedIndex]?.showUrl}
|
spotlightDisabled={sideOpen || panelInfo.modal}
|
||||||
>
|
|
||||||
<source
|
|
||||||
src={playListInfo && playListInfo[selectedIndex]?.showUrl}
|
src={playListInfo && playListInfo[selectedIndex]?.showUrl}
|
||||||
type="application/mpegurl"
|
style={panelInfo.modal ? modalStyle : {}}
|
||||||
/>
|
modalClassName={panelInfo.modal && panelInfo.modalClassName}
|
||||||
<track
|
>
|
||||||
kind="subtitles"
|
<source
|
||||||
src={vodSubtitleData && vodSubtitleData}
|
src={playListInfo && playListInfo[selectedIndex]?.showUrl}
|
||||||
srclang="en"
|
type="application/mpegurl"
|
||||||
label="English"
|
/>
|
||||||
type="text/vtt"
|
{/* <track
|
||||||
default
|
kind="subtitles"
|
||||||
/>
|
src={vodSubtitleData && vodSubtitleData}
|
||||||
{/* 테스트 */}
|
srclang="en"
|
||||||
{/* <source
|
label="English"
|
||||||
|
type="text/vtt"
|
||||||
|
default
|
||||||
|
/> */}
|
||||||
|
{/* 테스트 */}
|
||||||
|
{/* <source
|
||||||
src={
|
src={
|
||||||
" https://d130labfjte6iy.cloudfront.net/vod/1e62b39b-4301-4fe5-a16e-bebebef4e1c6/3e72500af7fc4d9d800e7a9533a29a36.mp4"
|
" https://d130labfjte6iy.cloudfront.net/vod/1e62b39b-4301-4fe5-a16e-bebebef4e1c6/3e72500af7fc4d9d800e7a9533a29a36.mp4"
|
||||||
}
|
}
|
||||||
@@ -297,9 +329,9 @@ const PlayerPanel = ({ hideChildren, isTabActivated, ...props }) => {
|
|||||||
label="English"
|
label="English"
|
||||||
type="text/vtt"
|
type="text/vtt"
|
||||||
/> */}
|
/> */}
|
||||||
</VideoPlayer>
|
</VideoPlayer>
|
||||||
{/* 리액트 플레이어 테스트용 */}
|
{/* 리액트 플레이어 테스트용 */}
|
||||||
{typeof window === "object" && window.PalmSystem ? (
|
{/* {typeof window === "object" && window.PalmSystem ? (
|
||||||
""
|
""
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
@@ -314,7 +346,7 @@ const PlayerPanel = ({ hideChildren, isTabActivated, ...props }) => {
|
|||||||
<SpottableBtn className={css.videoReduce} onClick={onClickBack} />
|
<SpottableBtn className={css.videoReduce} onClick={onClickBack} />
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)} */}
|
||||||
{playListInfo && playListInfo[selectedIndex]?.orderPhnNo && (
|
{playListInfo && playListInfo[selectedIndex]?.orderPhnNo && (
|
||||||
<VideoOverlayWithPhoneNumber
|
<VideoOverlayWithPhoneNumber
|
||||||
className={css.videoOverlayWithPhoneNumber}
|
className={css.videoOverlayWithPhoneNumber}
|
||||||
@@ -362,7 +394,6 @@ const PlayerPanel = ({ hideChildren, isTabActivated, ...props }) => {
|
|||||||
<LiveChannelContents
|
<LiveChannelContents
|
||||||
setSelectedIndex={setSelectedIndex}
|
setSelectedIndex={setSelectedIndex}
|
||||||
liveInfos={playListInfo}
|
liveInfos={playListInfo}
|
||||||
setPaneInfo={setPaneInfo}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@@ -375,4 +406,20 @@ const PlayerPanel = ({ hideChildren, isTabActivated, ...props }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default PlayerPanel;
|
const propsAreEqual = (prev, next) => {
|
||||||
|
const keys = Object.keys(prev);
|
||||||
|
const nextKeys = Object.keys(next);
|
||||||
|
if(keys.length !== nextKeys.length){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for(let i=0; i<keys.length; i++){
|
||||||
|
if(prev[keys[i]] !== next[keys[i]]){
|
||||||
|
if (JSON.stringify(prev[keys[i]]) === JSON.stringify(next[keys[i]])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
export default React.memo(PlayerPanel, propsAreEqual);
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
@import "../../style/CommonStyle.module.less";
|
@import "../../style/CommonStyle.module.less";
|
||||||
@import "../../style/utils.module.less";
|
@import "../../style/utils.module.less";
|
||||||
|
|
||||||
|
@videoBackgroundColor: black;
|
||||||
.videoContainer {
|
.videoContainer {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
background-color: @videoBackgroundColor;
|
||||||
.playButton {
|
.playButton {
|
||||||
.size(@w: 60px, @h: 60px);
|
.size(@w: 60px, @h: 60px);
|
||||||
background-image: url("../../../assets/images/btn/btn-video-play-nor@3x.png");
|
background-image: url("../../../assets/images/btn/btn-video-play-nor@3x.png");
|
||||||
@@ -156,4 +157,19 @@
|
|||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
&.modal,
|
||||||
|
&.background{
|
||||||
|
width:1px;
|
||||||
|
height: 1px;
|
||||||
|
left: -1px;
|
||||||
|
top: -1px;
|
||||||
|
pointer-events: none;
|
||||||
|
background-color: @videoBackgroundColor;
|
||||||
|
.videoOverlayWithPhoneNumber,
|
||||||
|
.tabContainer,
|
||||||
|
.arrow,
|
||||||
|
.toOpenBtn{
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ export default function ShopNowContents({ shopNowInfo }) {
|
|||||||
panelInfo: {
|
panelInfo: {
|
||||||
patnrId,
|
patnrId,
|
||||||
prdtId,
|
prdtId,
|
||||||
|
launchedFromPlayer: true
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user