player tab featured livechannel 구현

This commit is contained in:
고동영
2024-03-28 11:22:17 +09:00
parent 84cc39daaf
commit db33235636
9 changed files with 407 additions and 13 deletions

View File

@@ -0,0 +1,104 @@
import React, { memo, useCallback } from "react";
import classNames from "classnames";
import Spottable from "@enact/spotlight/Spottable";
import CustomImage from "../../../components/CustomImage/CustomImage";
import { $L } from "../../../utils/helperMethods";
import css from "./PlayerItemCard.module.less";
const SpottableComponent = Spottable("div");
const TYPES = {
liveHorizontal: "liveHorizontal",
featuredHorizontal: "featuredHorizontal",
};
const IMAGETYPES = {
imgHorizontal: "imgHorizontal",
imgVertical: "imgVertical",
};
// @@pyh Todo, 추후 다국어 resource 추가
const STRING_CONF = {
SOLD_OUT: $L("SOLD OUT"),
TOP: $L("TOP"),
};
export const removeDotAndColon = (string) => {
return /[.:]/.test(string) ? string.replace(/[.:]/g, "") : string;
};
export default memo(function TItemCard({
children,
disabled,
imageAlt,
imageSource,
imgType = IMAGETYPES.imgHorizontal,
logo,
onBlur,
onClick,
onFocus,
productId,
productName,
soldoutFlag,
spotlightId,
patnerName,
type = TYPES.liveHorizontal,
...rest
}) {
const _onBlur = useCallback(() => {
if (onBlur) {
onBlur();
}
}, [onBlur]);
const _onClick = useCallback(
(e) => {
if (disabled) {
e.stopPropagation();
return;
}
if (onClick) {
onClick(e);
}
},
[onClick, disabled]
);
const _onFocus = useCallback(() => {
if (onFocus) {
onFocus();
}
}, [onFocus]);
return (
<SpottableComponent
className={classNames(css[type])}
onBlur={_onBlur}
onClick={_onClick}
onFocus={_onFocus}
spotlightId={
productId ? "spotlightId-" + removeDotAndColon(productId) : spotlightId
}
{...rest}
>
<div className={css.imageWrap}>
<CustomImage alt={imageAlt} delay={0} src={imageSource} />
{soldoutFlag && soldoutFlag === "Y" && (
<div>{STRING_CONF.SOLD_OUT}</div>
)}
</div>
<div className={css.descWrap}>
<div className={css.patnerWrap}>
<img className={css.logo} src={logo} alt="" />
<h3 className={css.brandName}>{patnerName}</h3>
</div>
<h3 className={css.title}>{productName}</h3>
</div>
</SpottableComponent>
);
});
export { IMAGETYPES, TYPES };

View File

@@ -0,0 +1,144 @@
@import "../../../style/CommonStyle.module.less";
@import "../../../style/utils.module.less";
/* liveHorizontal */
.liveHorizontal {
display: flex;
.size(@w: 600px, @h: 236px);
padding: 18px;
border-radius: 12px;
border: solid 1px @COLOR_GRAY02;
background-color: @COLOR_WHITE;
.imageWrap {
position: relative;
.size(@w: 200px, @h: 200px);
margin-right: 20px;
color: @COLOR_WHITE;
> img {
.size(@w: inherit, @h: inherit);
object-fit: cover;
border: solid 1px #f0f0f0;
}
// sold out
> div {
.position(@position: absolute, @top: 0, @right: 0);
.flex();
.size(@w: 200px, @h: 200px);
background-color: rgba(26, 26, 26, 0.6);
font-weight: bold;
font-size: 36px;
}
}
.descWrap {
.flex(@direction: column, @justifyCenter: space-between, @alignCenter: flex-start);
width: 404px;
padding: 12px 0;
.patnerWrap {
display: flex;
margin-bottom: 7px;
> img {
.size(@w: 42px , @h: 42px);
margin-right: 11px;
}
> h3 {
font-size: 30px;
color: #767676;
font-weight: 600;
margin-top: 5px;
}
}
> h3 {
font-weight: bold;
font-size: 24px;
color: @COLOR_GRAY06;
.elip(@clamp:2);
word-break: break-all;
overflow: hidden;
margin-bottom: auto;
}
}
// focused
&:focus {
&::after {
.focused(@boxShadow:22px, @borderRadius:12px);
}
}
}
// featuredHorizontal
.featuredHorizontal {
display: flex;
.size(@w: 600px, @h: 176px);
padding: 18px;
border-radius: 12px;
border: solid 1px @COLOR_GRAY02;
background-color: @COLOR_WHITE;
.imageWrap {
position: relative;
.size(@w: 248px, @h: 140px);
margin-right: 20px;
color: @COLOR_WHITE;
> img {
.size(@w: inherit, @h: inherit);
object-fit: cover;
border: solid 1px #f0f0f0;
}
// sold out
> div {
.position(@position: absolute, @top: 0, @right: 0);
.flex();
.size(@w: 200px, @h: 200px);
background-color: rgba(26, 26, 26, 0.6);
font-weight: bold;
font-size: 36px;
}
}
.descWrap {
.flex(@direction: column, @justifyCenter: space-between, @alignCenter: flex-start);
width: 404px;
padding: 12px 0;
.patnerWrap {
display: flex;
margin-bottom: 7px;
> img {
.size(@w: 42px , @h: 42px);
margin-right: 11px;
}
> h3 {
font-size: 30px;
color: #767676;
font-weight: 600;
margin-top: 5px;
}
}
> h3 {
font-weight: bold;
font-size: 24px;
color: @COLOR_GRAY06;
.elip(@clamp:2);
word-break: break-all;
overflow: hidden;
margin-bottom: auto;
}
}
// focused
&:focus {
&::after {
.focused(@boxShadow:22px, @borderRadius:12px);
}
}
}

View File

@@ -31,7 +31,9 @@ import { VideoPlayer } from "../../components/VideoPlayer/VideoPlayer";
import { panel_names } from "../../utils/Config";
import { $L } from "../../utils/helperMethods";
import css from "./PlayerPanel.module.less";
import PlayerTabLiveContents from "./PlayerTabContents/PlayerTabLiveContents";
import FeaturedShowContents from "./PlayerTabContents/FeaturedShowContents";
import LiveChannelContents from "./PlayerTabContents/LiveChannelContents";
import ShopNowContents from "./PlayerTabContents/ShopNowContents";
import YouMayLikeContents from "./PlayerTabContents/YouMayLikeContents";
const SpottableBtn = Spottable("button");
@@ -44,7 +46,6 @@ const Container = SpotlightContainerDecorator(
const unableToPlay = new Job((callback) => {
// callback();
}, 5000);
const tabList = [$L("SHOP NOW"), $L("LIVE CHANNEL")];
const PlayerPanel = ({ hideChildren, isTabActivated, ...props }) => {
const dispatch = useDispatch();
@@ -54,14 +55,14 @@ const PlayerPanel = ({ hideChildren, isTabActivated, ...props }) => {
const [showItem, setShowItem] = useState(true);
const [sideOpen, setSideOpen] = useState(false);
const [selectedIndex, setSelectedIndex] = useState(0);
const liveChannelInfos = useSelector((state) => state.main.liveChannelInfos);
const featuredShowsInfos = useSelector(
(state) => state.main.featuredShowsInfos
);
const showNowProduct = useSelector((state) => state.main.showNowProduct);
const fullVideoData = useSelector((state) => state.main.fullVideoData);
const panels = useSelector((state) => state.panels.panels);
const tabList = [
$L("SHOP NOW"),
$L(panelInfo.type === "LIVE" ? "LIVE CHANNEL" : "FEATURED SHOWS"),
];
const onClickBack = useCallback(
(ev) => {
console.log("onClickBack ev...", ev);
@@ -242,10 +243,12 @@ const PlayerPanel = ({ hideChildren, isTabActivated, ...props }) => {
listType={LIST_TYPE.small}
/>
{panelInfo.type === "LIVE" && tab === 0 && <PlayerTabLiveContents />}
{showNowProduct && showNowProduct.length < 3 && (
{tab === 0 && <ShopNowContents />}
{showNowProduct && showNowProduct.length < 3 && tab === 0 && (
<YouMayLikeContents />
)}
{panelInfo.type === "LIVE" && tab === 1 && <LiveChannelContents />}
{panelInfo.type === "VOD" && tab === 1 && <FeaturedShowContents />}
</Container>
)}
</TPanel>

View File

@@ -0,0 +1,58 @@
import React, { useCallback, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { pushPanel } from "../../../actions/panelActions";
import TVirtualGridList from "../../../components/TVirtualGridList/TVirtualGridList";
import { panel_names } from "../../../utils/Config";
import PlayerItemCard, { TYPES } from "../PlayerItemCard/PlayerItemCard";
import css from "./LiveChannelContents.module.less";
export default function FeaturedShowContents() {
const dispatch = useDispatch();
const featuredShowsInfos = useSelector(
(state) => state.main.featuredShowsInfos
);
console.log("#featuredShowsInfos", featuredShowsInfos);
const renderItem = useCallback(
({ index, ...rest }) => {
const { thumbnailUrl, patncLogoPath, patnrId, prdtId, patncNm, showNm } =
featuredShowsInfos[index];
const handleItemClick = () => {};
return (
<PlayerItemCard
{...rest}
key={prdtId}
imageAlt={prdtId}
logo={patncLogoPath}
imageSource={thumbnailUrl}
productName={showNm}
patnerName={patncNm}
onClick={handleItemClick}
type={TYPES.featuredHorizontal}
/>
);
},
[featuredShowsInfos]
);
return (
<>
<div className={css.container}>
{featuredShowsInfos && featuredShowsInfos.length > 0 && (
<TVirtualGridList
dataSize={featuredShowsInfos.length}
direction="vertical"
renderItem={renderItem}
itemWidth={600}
itemHeight={176}
spacing={12}
className={css.itemList}
/>
)}
</div>
</>
);
}

View File

@@ -0,0 +1,10 @@
@import "../../../style/utils.module.less";
.container {
height: 956px;
overflow: hidden;
> div:nth-child(1) {
.size(@w: 100%, @h: inherit);
}
}

View File

@@ -0,0 +1,64 @@
import React, { useCallback, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { pushPanel } from "../../../actions/panelActions";
import TVirtualGridList from "../../../components/TVirtualGridList/TVirtualGridList";
import { panel_names } from "../../../utils/Config";
import PlayerItemCard, { TYPES } from "../PlayerItemCard/PlayerItemCard";
import css from "./LiveChannelContents.module.less";
export default function LiveChannelContents() {
const dispatch = useDispatch();
const liveChannelInfos = useSelector((state) => state.main.liveChannelInfos);
console.log("#liveChannelInfos", liveChannelInfos);
const renderItem = useCallback(
({ index, ...rest }) => {
const {
dfltThumbnailImgPath,
patncLogoPath,
patnrId,
prdtId,
prdtNm,
priceInfo,
offerInfo,
patncNm,
} = liveChannelInfos[index];
const handleItemClick = () => {};
return (
<PlayerItemCard
{...rest}
key={prdtId}
imageAlt={prdtId}
logo={patncLogoPath}
imageSource={dfltThumbnailImgPath}
productName={prdtNm}
patnerName={patncNm}
onClick={handleItemClick}
type={TYPES.liveHorizontal}
/>
);
},
[liveChannelInfos]
);
return (
<>
<div className={css.container}>
{liveChannelInfos && liveChannelInfos.length > 0 && (
<TVirtualGridList
dataSize={liveChannelInfos.length}
direction="vertical"
renderItem={renderItem}
itemWidth={600}
itemHeight={236}
spacing={12}
className={css.itemList}
/>
)}
</div>
</>
);
}

View File

@@ -0,0 +1,10 @@
@import "../../../style/utils.module.less";
.container {
height: 956px;
overflow: hidden;
> div:nth-child(1) {
.size(@w: 100%, @h: inherit);
}
}

View File

@@ -8,13 +8,14 @@ import { pushPanel } from "../../../actions/panelActions";
import TItemCard, { TYPES } from "../../../components/TItemCard/TItemCard";
import TVirtualGridList from "../../../components/TVirtualGridList/TVirtualGridList";
import { panel_names } from "../../../utils/Config";
import css from "./PlayerTabLiveContents.module.less";
import css from "./ShopNowContents.module.less";
export default function PlayerTabContents() {
export default function ShopNowContents() {
const dispatch = useDispatch();
const showNowProduct = useSelector((state) => state.main.showNowProduct);
const [height, setHeight] = useState();
console.log("#showNowProduct", showNowProduct);
const gridStyle = useMemo(() => ({ height: `${height}px` }), [height]);
useEffect(() => {
@@ -27,7 +28,7 @@ export default function PlayerTabContents() {
const renderItem = useCallback(
({ index, ...rest }) => {
const { thumbnailUrl, patnrId, prdtId, prdtNm, priceInfo, offerInfo } =
const { imgUrls600, patnrId, prdtId, prdtNm, priceInfo, offerInfo } =
showNowProduct[index];
const handleItemClick = () => {
@@ -47,7 +48,7 @@ export default function PlayerTabContents() {
{...rest}
key={prdtId}
imageAlt={prdtId}
imageSource={thumbnailUrl}
imageSource={imgUrls600[0]}
priceInfo={priceInfo}
offerInfo={offerInfo}
productName={prdtNm}