[DetailPanel] themeIndicator css 수정 & Media 에서 보이게 수정

This commit is contained in:
고동영
2024-05-20 18:41:06 +09:00
parent 31310f0912
commit 2e77990620
13 changed files with 303 additions and 220 deletions

View File

@@ -106,6 +106,7 @@ export const types = {
GET_BEST_SELLER: "GET_BEST_SELLER",
GET_PRODUCT_GROUP: "GET_PRODUCT_GROUP",
GET_PRODUCT_OPTION: "GET_PRODUCT_OPTION",
GET_PRODUCT_IMAGE_LENGTH: "GET_PRODUCT_IMAGE_LENGTH",
// search actions
GET_SEARCH: "GET_SEARCH",

View File

@@ -90,3 +90,12 @@ export const getProductOption = (props) => (dispatch, getState) => {
export const clearProductDetail = () => ({
type: types.CLEAR_PRODUCT_DETAIL,
});
export const getProductImageLength =
({ imageLength }) =>
(dispatch) => {
dispatch({
type: types.GET_PRODUCT_IMAGE_LENGTH,
payload: imageLength,
});
};

View File

@@ -7,18 +7,15 @@
* @exports VideoPlayerBase
*/
import React from 'react';
import ReactDOM from 'react-dom';
import React from "react";
import ReactDOM from "react-dom";
import classNames from 'classnames';
import DurationFmt from 'ilib/lib/DurationFmt';
import PropTypes from 'prop-types';
import shallowEqual from 'recompose/shallowEqual';
import classNames from "classnames";
import DurationFmt from "ilib/lib/DurationFmt";
import PropTypes from "prop-types";
import shallowEqual from "recompose/shallowEqual";
import {
off,
on,
} from '@enact/core/dispatcher';
import { off, on } from "@enact/core/dispatcher";
import {
adaptEvent,
call,
@@ -29,57 +26,46 @@ import {
preventDefault,
returnsTrue,
stopImmediate,
} from '@enact/core/handle';
import ApiDecorator from '@enact/core/internal/ApiDecorator';
import EnactPropTypes from '@enact/core/internal/prop-types';
import { is } from '@enact/core/keymap';
import { platform } from '@enact/core/platform';
import {
Job,
memoize,
perfNow,
} from '@enact/core/util';
import { I18nContextDecorator } from '@enact/i18n/I18nDecorator';
import { toUpperCase } from '@enact/i18n/util';
import Skinnable from '@enact/sandstone/Skinnable';
import {
getDirection,
Spotlight,
} from '@enact/spotlight';
import {
SpotlightContainerDecorator,
} from '@enact/spotlight/SpotlightContainerDecorator';
import { Spottable } from '@enact/spotlight/Spottable';
import Announce from '@enact/ui/AnnounceDecorator/Announce';
import ComponentOverride from '@enact/ui/ComponentOverride';
import { FloatingLayerDecorator } from '@enact/ui/FloatingLayer';
import {
FloatingLayerContext,
} from '@enact/ui/FloatingLayer/FloatingLayerDecorator';
import Slottable from '@enact/ui/Slottable';
import Touchable from '@enact/ui/Touchable';
} from "@enact/core/handle";
import ApiDecorator from "@enact/core/internal/ApiDecorator";
import EnactPropTypes from "@enact/core/internal/prop-types";
import { is } from "@enact/core/keymap";
import { platform } from "@enact/core/platform";
import { Job, memoize, perfNow } from "@enact/core/util";
import { I18nContextDecorator } from "@enact/i18n/I18nDecorator";
import { toUpperCase } from "@enact/i18n/util";
import Skinnable from "@enact/sandstone/Skinnable";
import { getDirection, Spotlight } from "@enact/spotlight";
import { SpotlightContainerDecorator } from "@enact/spotlight/SpotlightContainerDecorator";
import { Spottable } from "@enact/spotlight/Spottable";
import Announce from "@enact/ui/AnnounceDecorator/Announce";
import ComponentOverride from "@enact/ui/ComponentOverride";
import { FloatingLayerDecorator } from "@enact/ui/FloatingLayer";
import { FloatingLayerContext } from "@enact/ui/FloatingLayer/FloatingLayerDecorator";
import Slottable from "@enact/ui/Slottable";
import Touchable from "@enact/ui/Touchable";
import { $L } from '../../utils/helperMethods';
import { SpotlightIds } from '../../utils/SpotlightIds';
import PlayerOverlayContents
from '../../views/PlayerPanel/PlayerOverlay/PlayerOverlayContents';
import Loader from '../Loader/Loader';
import { $L } from "../../utils/helperMethods";
import { SpotlightIds } from "../../utils/SpotlightIds";
import ThemeIndicator from "../../views/DetailPanel/components/indicator/ThemeIndicator";
import ThemeIndicatorArrow from "../../views/DetailPanel/components/indicator/ThemeIndicatorArrow";
import PlayerOverlayContents from "../../views/PlayerPanel/PlayerOverlay/PlayerOverlayContents";
import Loader from "../Loader/Loader";
import {
MediaControls,
MediaSlider,
secondsToTime,
Times,
} from '../MediaPlayer';
import VideoOverlayWithPhoneNumber
from '../VideoOverlayWithPhoneNumber/VideoOverlayWithPhoneNumber';
import FeedbackContent from './FeedbackContent';
import FeedbackTooltip from './FeedbackTooltip';
import Media from './Media';
import MediaTitle from './MediaTitle';
import Overlay from './Overlay';
import TReactPlayer from './TReactPlayer';
import Video from './Video';
import css from './VideoPlayer.module.less';
} from "../MediaPlayer";
import VideoOverlayWithPhoneNumber from "../VideoOverlayWithPhoneNumber/VideoOverlayWithPhoneNumber";
import FeedbackContent from "./FeedbackContent";
import FeedbackTooltip from "./FeedbackTooltip";
import Media from "./Media";
import MediaTitle from "./MediaTitle";
import Overlay from "./Overlay";
import TReactPlayer from "./TReactPlayer";
import Video from "./Video";
import css from "./VideoPlayer.module.less";
const isEnter = is("enter");
const isLeft = is("left");
@@ -2093,6 +2079,8 @@ const VideoPlayerBase = class extends React.Component {
mediaDisclaimer,
liveTotalTime,
currentLiveTimeSeconds,
themeProductInfos,
detailThemeProductImageLength,
orderPhnNo,
...mediaProps
} = this.props;
@@ -2180,6 +2168,12 @@ const VideoPlayerBase = class extends React.Component {
<div className={css.disclaimer}> {mediaDisclaimer}</div>
</div>
)}
{panelInfo.modal && type === "MEDIA" && (
<ThemeIndicatorArrow
themeProductInfos={themeProductInfos}
imageLength={detailThemeProductImageLength}
/>
)}
{panelInfo.modal && orderPhnNo && type !== "MEDIA" && (
<VideoOverlayWithPhoneNumber
className={css.videoOverlayWithPhoneNumber}

View File

@@ -14,6 +14,16 @@
height: 100%;
width: 100%;
background: #000;
&:after {
width: 560px;
height: 200px;
position: absolute;
left: 0;
bottom: 0;
content: "";
background: linear-gradient(to top, rgba(255, 255, 255, 1), transparent);
opacity: 0.2;
}
}
.preloadVideo {
@@ -29,6 +39,7 @@
width: 100%;
}
}
.videoOverlayWithPhoneNumber {
width: 250px;
height: 106px;

View File

@@ -2,6 +2,7 @@ import { types } from "../actions/actionTypes";
const initialState = {
bestSellerData: {},
productImageLength: 0,
};
export const productReducer = (state = initialState, action) => {
@@ -21,6 +22,11 @@ export const productReducer = (state = initialState, action) => {
...state,
groupInfo: action.payload.groupInfo,
};
case types.GET_PRODUCT_IMAGE_LENGTH:
return {
...state,
productImageLength: action.payload + 1,
};
default:
return state;
}

View File

@@ -15,6 +15,7 @@ import { finishVideoPreview } from "../../actions/playActions";
import {
clearProductDetail,
getProductGroup,
getProductImageLength,
} from "../../actions/productActions";
import TBody from "../../components/TBody/TBody";
import THeader from "../../components/THeader/THeader";
@@ -186,6 +187,22 @@ export default function DetailPanel({ panelInfo, isOnTop, spotlightId }) {
}
}, [panelInfo]);
useEffect(() => {
if (
((themeProductInfos && themeProductInfos.length > 0) ||
(hotelInfos && hotelInfos.length > 0)) &&
selectedIndex !== undefined
) {
if (type === "theme") {
const imgUrls600 = themeProductInfos[selectedIndex]?.imgUrls600 || [];
dispatch(getProductImageLength({ imageLength: imgUrls600.length }));
return;
}
const imgUrls600 = hotelInfos[selectedIndex]?.imgUrls600 || [];
dispatch(getProductImageLength({ imageLength: imgUrls600.length }));
}
}, [dispatch, themeProductInfos, selectedIndex, hotelInfos]);
return (
<>
<TPanel

View File

@@ -1,9 +1,10 @@
import React, { useEffect, useMemo, useState } from "react";
import { useSelector } from "react-redux";
import { useDispatch, useSelector } from "react-redux";
import SpotlightContainerDecorator from "@enact/spotlight/SpotlightContainerDecorator";
import { getProductImageLength } from "../../../actions/productActions";
import IndicatorOptions from "../components/indicator/IndicatorOptions";
import ThemeIndicator from "../components/indicator/ThemeIndicator";
import ShowSingleOption from "./ShowOptions/ShowSingleOption";
@@ -28,6 +29,8 @@ export default function ShowOption({
);
const productData = useSelector((state) => state.home.productData);
const dispatch = useDispatch();
const showProductInfo = useMemo(() => {
if (productData && productInfo) {
const themeInfo = productData?.themeInfo[0];

View File

@@ -63,6 +63,7 @@ function Indicator({
}
if (autoPlaying && productInfo?.prdtMediaUrl) {
//auto play.
console.log("#autoPlay indicator");
dispatch(
startVideoPlayer({
showUrl: productInfo?.prdtMediaUrl,
@@ -73,7 +74,7 @@ function Indicator({
disclaimer: productInfo?.disclaimer,
subtitle: productInfo?.prdtMediaSubtitlUrl,
shptmBanrTpNm: "MEDIA",
modal: autoPlaying,
modal: isAutoPlaying,
modalContainerId: "indicator_videoContainer", //to calc width, height, left, top
modalClassName: modalClassNameChange(),
spotlightDisable: true,
@@ -82,6 +83,13 @@ function Indicator({
}
}, [dispatch, productInfo, autoPlaying, focused, selectedIndex]);
const isAutoPlaying = useMemo(() => {
if (autoPlaying) {
return true;
}
return false;
}, [autoPlaying]);
const modalClassNameChange = useCallback(() => {
if (selectedIndex === 0) {
if (focused) {

View File

@@ -1,31 +1,21 @@
import React, {
useCallback,
useEffect,
useMemo,
useState,
} from 'react';
import React, { useCallback, useEffect, useMemo, useState } from "react";
import classNames from 'classnames';
import {
useDispatch,
useSelector,
} from 'react-redux';
import classNames from "classnames";
import { useDispatch, useSelector } from "react-redux";
import Image from '@enact/sandstone/Image';
import Spotlight from '@enact/spotlight';
import SpotlightContainerDecorator
from '@enact/spotlight/SpotlightContainerDecorator';
import Spottable from '@enact/spotlight/Spottable';
import Image from "@enact/sandstone/Image";
import Spotlight from "@enact/spotlight";
import SpotlightContainerDecorator from "@enact/spotlight/SpotlightContainerDecorator";
import Spottable from "@enact/spotlight/Spottable";
import defaultImage
from '../../../../../assets/images/img-thumb-empty-144@3x.png';
import { startVideoPlayer } from '../../../../actions/playActions';
import TVirtualGridList
from '../../../../components/TVirtualGridList/TVirtualGridList';
import useScrollTo from '../../../../hooks/useScrollTo';
import { panel_names } from '../../../../utils/Config';
import { scaleW } from '../../../../utils/helperMethods';
import css from './ThemeIndicator.module.less';
import defaultImage from "../../../../../assets/images/img-thumb-empty-144@3x.png";
import { startVideoPlayer } from "../../../../actions/playActions";
import TVirtualGridList from "../../../../components/TVirtualGridList/TVirtualGridList";
import useScrollTo from "../../../../hooks/useScrollTo";
import { panel_names } from "../../../../utils/Config";
import { $L, scaleW } from "../../../../utils/helperMethods";
import css from "./ThemeIndicator.module.less";
import ThemeIndicatorArrow from "./ThemeIndicatorArrow";
const Container = SpotlightContainerDecorator(
{ enterTo: "last-focused", preserveld: true },
@@ -58,13 +48,14 @@ export default function ThemeIndicator({
const imageLength = thumbnailUrls && thumbnailUrls.length;
const topPanel = panels[panels.length - 1];
const { getScrollTo, scrollTop } = useScrollTo();
let imagePosition = IMAGE_WIDTH * selectedIndex - IMAGE_WIDTH;
let imagePosition = IMAGE_WIDTH * selectedIndex - IMAGE_WIDTH;
const getProductMediaUrlStatus = useCallback(() => {
return (
themeProductInfo?.prdtMediaUrl && themeProductInfo?.prdtMediaUrl !== null
);
}, [themeProductInfo]);
useEffect(() => {
if (thumbnailUrls) {
if (getProductMediaUrlStatus()) {
@@ -140,26 +131,6 @@ export default function ThemeIndicator({
}
}, [autoPlaying, themeProductInfo, imageSelectedIndex]);
const handlePrevClick = () => {
if (imageSelectedIndex > 0) {
setImageSelectedIndex((prev) => prev - 1);
}
};
const handleNextClick = () => {
const hasMediaUrl = getProductMediaUrlStatus();
if (hasMediaUrl && imageSelectedIndex + 1 < imageLength + 1) {
setImageSelectedIndex((prev) => prev + 1);
} else if (imageSelectedIndex + 1 < imageLength) {
setImageSelectedIndex((prev) => prev + 1);
}
if (imageLength === imageSelectedIndex) {
Spotlight.focus("");
}
};
const handleUpClick = useCallback(() => {
if (selectedIndex === 0) {
return;
@@ -288,17 +259,7 @@ export default function ThemeIndicator({
}
}, [canPlayVideo, imageSelectedIndex]);
// const currentSpotlightDisabled = useMemo(() => {
// if ( imageLength === (imageSelectedIndex || 1) ||
// (!themeProductInfo?.prdtMediaUrl &&
// imageLength - 1 === imageSelectedIndex)) {
// return true
// }
// }, [])
const renderThumbnail = useCallback(() => {
const hasMediaUrl = getProductMediaUrlStatus();
return (
<div className={css.thumbnailContainer}>
<SpottableComponent
@@ -316,55 +277,16 @@ export default function ThemeIndicator({
alt=""
/>
</SpottableComponent>
<div className={css.thumbnailIndicator}>
{soldoutFlag ? (
<h3>SOLD OUT</h3>
) : (
<>
<SpottableComponent
className={classNames(
css.prevButton,
imageSelectedIndex === 0 && css.disable
)}
onClick={handlePrevClick}
spotlightDisabled={
imageSelectedIndex === -1 ||
(!themeProductInfo?.prdtMediaUrl &&
imageSelectedIndex === 0) ||
(themeProductInfo?.prdtMediaUrl && imageSelectedIndex === 0)
}
spotlightId="thumbnailPrevButton"
/>
{hasMediaUrl ? (
<span>
{imageSelectedIndex + 1} / {imageLength + 1}
</span>
) : (
<span>
{imageSelectedIndex + 1} / {imageLength}
</span>
)}
<SpottableComponent
className={classNames(
css.nextButton,
imageLength === (imageSelectedIndex || 1) && css.disable,
!themeProductInfo?.prdtMediaUrl &&
imageLength - 1 === imageSelectedIndex &&
css.disable
)}
onClick={handleNextClick}
spotlightDisabled={
imageLength === (imageSelectedIndex || 1) ||
(!themeProductInfo?.prdtMediaUrl &&
imageLength - 1 === imageSelectedIndex &&
css.disable)
}
spotlightId="thumbnailNextButton"
/>
</>
)}
</div>
{soldoutFlag ? (
<h3>{$L("SOLD OUT")}</h3>
) : (
<ThemeIndicatorArrow
imageSelectedIndex={imageSelectedIndex}
setImageSelectedIndex={setImageSelectedIndex}
themeProductInfo={themeProductInfo}
imageLength={imageLength}
/>
)}
</div>
);
}, [

View File

@@ -38,63 +38,6 @@
}
}
.thumbnailIndicator {
width: 100%;
margin-top: auto;
position: absolute;
bottom: -5px;
margin-bottom: 30px;
z-index: 1;
text-align: center;
.prevButton {
.size(@w:42px, @h: 42px);
.position(@position: absolute, @left: 190px, @bottom: 0 , @top: 0);
background-image: url("../../../../../assets/images/btn/btn-prev-thumb-nor.svg");
background-position: center;
background-size: cover;
&:focus {
background-image: url("../../../../../assets/images/btn/btn-prev-thumb-foc.svg");
}
&.disable {
opacity: 0.1;
background-image: url("../../../../../assets/images/btn/btn-prev-thumb-nor.svg");
}
}
.nextButton {
.size(@w:42px, @h: 42px);
.position(@position: absolute, @right: 190px, @bottom: 0 , @top: 0);
background-image: url("../../../../../assets/images/btn/btn-next-thumb-nor.svg");
background-position: center;
background-size: cover;
&:focus {
background-image: url("../../../../../assets/images/btn/btn-next-thumb-foc.svg");
}
&.disable {
opacity: 0.1;
background-image: url("../../../../../assets/images/btn/btn-next-thumb-nor.svg");
}
}
//soldout
> h3 {
font-size: 36px;
font-weight: bold;
color: #ffffff;
margin-bottom: 237px;
}
> span {
// .size(@w: 51px , @h: 21px);
-webkit-text-stroke: 1px rgba(255, 255, 255, 0.7);
color: #222222;
font-size: 24px;
font-weight: bold;
line-height: 1.33;
}
}
.thumbnail {
.size(@w: 560px, @h: 560px);
box-shadow: 0 0 0 1px #dadada inset;

View File

@@ -0,0 +1,91 @@
import React, { useCallback, useMemo } from "react";
import classNames from "classnames";
import Spotlight from "@enact/spotlight";
import Spottable from "@enact/spotlight/Spottable";
import css from "./ThemeIndicatorArrow.module.less";
const SpottableComponent = Spottable("div");
export default function ThemeIndicatorArrow({
imageSelectedIndex = 0,
setImageSelectedIndex,
themeProductInfo,
imageLength,
}) {
const getProductMediaUrlStatus = useCallback(() => {
return (
themeProductInfo?.prdtMediaUrl && themeProductInfo?.prdtMediaUrl !== null
);
}, [themeProductInfo]);
const hasMediaUrl = getProductMediaUrlStatus();
const handlePrevClick = () => {
if (imageSelectedIndex > 0) {
setImageSelectedIndex((prev) => prev - 1);
}
};
const handleNextClick = () => {
if (hasMediaUrl && imageSelectedIndex + 1 < imageLength + 1) {
setImageSelectedIndex((prev) => prev + 1);
} else if (imageSelectedIndex + 1 < imageLength) {
setImageSelectedIndex((prev) => prev + 1);
}
if (imageLength === imageSelectedIndex) {
Spotlight.focus("");
}
};
return (
<>
<div className={css.thumbnailIndicator}>
<>
<SpottableComponent
className={classNames(
css.prevButton,
imageSelectedIndex === 0 && css.disable
)}
onClick={handlePrevClick}
spotlightDisabled={
imageSelectedIndex === -1 ||
(!themeProductInfo?.prdtMediaUrl && imageSelectedIndex === 0) ||
(themeProductInfo?.prdtMediaUrl && imageSelectedIndex === 0)
}
spotlightId="thumbnailPrevButton"
/>
{hasMediaUrl ? (
<span>
{imageSelectedIndex + 1} / {imageLength + 1}
</span>
) : (
<span>
{imageSelectedIndex + 1} / {imageLength}
</span>
)}
<SpottableComponent
className={classNames(
css.nextButton,
imageLength === (imageSelectedIndex || 1) && css.disable,
!themeProductInfo?.prdtMediaUrl &&
imageLength - 1 === imageSelectedIndex &&
css.disable
)}
onClick={handleNextClick}
spotlightDisabled={
imageLength === (imageSelectedIndex || 1) ||
(!themeProductInfo?.prdtMediaUrl &&
imageLength - 1 === imageSelectedIndex &&
css.disable)
}
spotlightId="thumbnailNextButton"
/>
</>
</div>
</>
);
}

View File

@@ -0,0 +1,59 @@
@import "../../../../style/CommonStyle.module.less";
@import "../../../../style/utils.module.less";
.thumbnailIndicator {
width: 100%;
margin-top: auto;
position: absolute;
bottom: -5px;
margin-bottom: 30px;
z-index: 1;
text-align: center;
.prevButton {
.size(@w:42px, @h: 42px);
.position(@position: absolute, @left: 190px, @bottom: 0 , @top: 0);
background-image: url("../../../../../assets/images/btn/btn-prev-thumb-nor.svg");
background-position: center;
background-size: cover;
&:focus {
background-image: url("../../../../../assets/images/btn/btn-prev-thumb-foc.svg");
}
&.disable {
opacity: 0.1;
background-image: url("../../../../../assets/images/btn/btn-prev-thumb-nor.svg");
}
}
.nextButton {
.size(@w:42px, @h: 42px);
.position(@position: absolute, @right: 190px, @bottom: 0 , @top: 0);
background-image: url("../../../../../assets/images/btn/btn-next-thumb-nor.svg");
background-position: center;
background-size: cover;
&:focus {
background-image: url("../../../../../assets/images/btn/btn-next-thumb-foc.svg");
}
&.disable {
opacity: 0.1;
background-image: url("../../../../../assets/images/btn/btn-next-thumb-nor.svg");
}
}
//soldout
> h3 {
font-size: 36px;
font-weight: bold;
color: #ffffff;
margin-bottom: 237px;
}
> span {
// .size(@w: 51px , @h: 21px);
-webkit-text-stroke: 1px rgba(255, 255, 255, 0.7);
color: #222222;
font-size: 24px;
font-weight: bold;
line-height: 1.33;
}
}

View File

@@ -67,7 +67,7 @@ const findSelector = (selector, maxAttempts = 5, currentAttempts = 0) => {
return findSelector(selector, maxAttempts, currentAttempts + 1);
}
} catch (error) {
console.error(error.message);
// console.error(error.message);
}
};
const PlayerPanel = ({
@@ -93,6 +93,15 @@ const PlayerPanel = ({
const panels = useSelector((state) => state.panels.panels);
const chatData = useSelector((state) => state.play.chatData);
const showDetailInfo = useSelector((state) => state.main.showDetailInfo);
const productImageLength = useSelector(
(state) => state.product.productImageLength
);
const themeProductInfos = useSelector(
(state) => state.home.themeCurationDetailInfoData
);
const hotelInfos = useSelector(
(state) => state.home.themeCurationHotelDetailData
);
const captionEnable = useSelector(
(state) => state.common.appStatus.captionEnable
);
@@ -107,10 +116,15 @@ const PlayerPanel = ({
const vodSubtitleData = useSelector((state) => state.play.subTitleBlobs);
const initialFocusTimeoutJob = useRef(new Job((func) => func(), 600));
console.log("#productImageLength", productImageLength);
const onClickBack = useCallback(
(ev) => {
//modal로부터 Full 전환된 경우 다시 preview 모드로 돌아감.
console.log("####onClickBack onEnded1111", panelInfo.modal);
console.log("#panelInfo", panelInfo);
if (panelInfo.modalContainerId && !panelInfo.modal) {
console.log("####onClickBack onEnded22222");
dispatch(
startVideoPlayer({
...panelInfo,
@@ -124,6 +138,7 @@ const PlayerPanel = ({
return;
}
if (!panelInfo.modal) {
console.log("####onClickBack onEnded3333333");
dispatch(PanelActions.popPanel());
ev?.stopPropagation();
ev?.preventDefault();
@@ -583,6 +598,10 @@ const PlayerPanel = ({
smallestOffsetHourIndex &&
shopNowInfo[smallestOffsetHourIndex]?.disclaimer
}
themeProductInfos={
themeProductInfos ? themeProductInfos : hotelInfos
}
detailThemeProductImageLength={productImageLength}
shopNowInfos={shopNowInfo}
playListInfo={playListInfo && playListInfo}
selectedIndex={selectedIndex}