[251019] fix: DetailPanel -> ProductAlLSection 백그라운드 비디오 제어
🕐 커밋 시간: 2025. 10. 19. 16:37:58 📊 변경 통계: • 총 파일: 4개 • 추가: +129줄 • 삭제: -13줄 📝 수정된 파일: ~ com.twin.app.shoptime/src/actions/playActions.js ~ com.twin.app.shoptime/src/views/DetailPanel/DetailPanel.jsx ~ com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/ProductVideo/ProductVideo.v2.jsx ~ com.twin.app.shoptime/src/views/PlayerPanel/PlayerPanel.jsx 🔧 함수 변경 내용: 📄 com.twin.app.shoptime/src/actions/playActions.js (javascript): ✅ Added: resumeModalVideo() 🔧 주요 변경 내용: • 핵심 비즈니스 로직 개선
This commit is contained in:
@@ -139,13 +139,20 @@ export const resumeModalVideo = () => (dispatch, getState) => {
|
||||
export const pauseFullscreenVideo = () => (dispatch, getState) => {
|
||||
const panels = getState().panels.panels;
|
||||
|
||||
// console.log('[BgVideo] pauseFullscreenVideo called - panels:', {
|
||||
// panelsCount: panels?.length,
|
||||
// panels: panels?.map(p => ({ name: p.name, modal: p.panelInfo?.modal, isPaused: p.panelInfo?.isPaused }))
|
||||
// });
|
||||
|
||||
// 전체화면 PlayerPanel 찾기 (modal이 false인 패널)
|
||||
const fullscreenPlayerPanel = panels.find(
|
||||
(panel) => panel.name === panel_names.PLAYER_PANEL && !panel.panelInfo?.modal
|
||||
);
|
||||
|
||||
// console.log('[BgVideo] pauseFullscreenVideo - fullscreenPlayerPanel found:', !!fullscreenPlayerPanel);
|
||||
|
||||
if (fullscreenPlayerPanel) {
|
||||
console.log('[pauseFullscreenVideo] Pausing fullscreen video');
|
||||
// console.log('[BgVideo] pauseFullscreenVideo - dispatching updatePanel with isPaused: true');
|
||||
dispatch(
|
||||
updatePanel({
|
||||
name: panel_names.PLAYER_PANEL,
|
||||
@@ -155,6 +162,8 @@ export const pauseFullscreenVideo = () => (dispatch, getState) => {
|
||||
},
|
||||
})
|
||||
);
|
||||
} else {
|
||||
console.log('[BgVideo] pauseFullscreenVideo - No fullscreen PlayerPanel found, skipping');
|
||||
}
|
||||
};
|
||||
|
||||
@@ -162,13 +171,21 @@ export const pauseFullscreenVideo = () => (dispatch, getState) => {
|
||||
export const resumeFullscreenVideo = () => (dispatch, getState) => {
|
||||
const panels = getState().panels.panels;
|
||||
|
||||
// console.log('[BgVideo] resumeFullscreenVideo called - panels:', {
|
||||
// panelsCount: panels?.length,
|
||||
// panels: panels?.map(p => ({ name: p.name, modal: p.panelInfo?.modal, isPaused: p.panelInfo?.isPaused }))
|
||||
// });
|
||||
|
||||
// 전체화면 PlayerPanel 찾기 (modal이 false인 패널)
|
||||
const fullscreenPlayerPanel = panels.find(
|
||||
(panel) => panel.name === panel_names.PLAYER_PANEL && !panel.panelInfo?.modal
|
||||
);
|
||||
|
||||
// console.log('[BgVideo] resumeFullscreenVideo - fullscreenPlayerPanel found:', !!fullscreenPlayerPanel);
|
||||
// console.log('[BgVideo] resumeFullscreenVideo - isPaused:', fullscreenPlayerPanel?.panelInfo?.isPaused);
|
||||
|
||||
if (fullscreenPlayerPanel && fullscreenPlayerPanel.panelInfo?.isPaused) {
|
||||
console.log('[resumeFullscreenVideo] Resuming fullscreen video');
|
||||
// console.log('[BgVideo] resumeFullscreenVideo - dispatching updatePanel with isPaused: false');
|
||||
dispatch(
|
||||
updatePanel({
|
||||
name: panel_names.PLAYER_PANEL,
|
||||
@@ -178,6 +195,8 @@ export const resumeFullscreenVideo = () => (dispatch, getState) => {
|
||||
},
|
||||
})
|
||||
);
|
||||
} else {
|
||||
console.log('[BgVideo] resumeFullscreenVideo - Not resuming (not found or not paused)');
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,116 +1,89 @@
|
||||
// src/views/DetailPanel/DetailPanel.new.jsx
|
||||
import React, {
|
||||
useCallback,
|
||||
useEffect,
|
||||
useLayoutEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
} from "react";
|
||||
import React, { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
|
||||
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
|
||||
import Spinner from "@enact/sandstone/Spinner";
|
||||
import Spotlight from "@enact/spotlight";
|
||||
import { setContainerLastFocusedElement } from "@enact/spotlight/src/container";
|
||||
import Spinner from '@enact/sandstone/Spinner';
|
||||
import Spotlight from '@enact/spotlight';
|
||||
import { setContainerLastFocusedElement } from '@enact/spotlight/src/container';
|
||||
|
||||
import indicatorDefaultImage from "../../../assets/images/img-thumb-empty-144@3x.png";
|
||||
import { getDeviceAdditionInfo } from "../../actions/deviceActions";
|
||||
import { getThemeCurationDetailInfo } from "../../actions/homeActions";
|
||||
import indicatorDefaultImage from '../../../assets/images/img-thumb-empty-144@3x.png';
|
||||
import { getDeviceAdditionInfo } from '../../actions/deviceActions';
|
||||
import { getThemeCurationDetailInfo } from '../../actions/homeActions';
|
||||
import { getMainCategoryDetail, getMainYouMayLike } from '../../actions/mainActions';
|
||||
import { popPanel, updatePanel } from '../../actions/panelActions';
|
||||
import {
|
||||
getMainCategoryDetail,
|
||||
getMainYouMayLike,
|
||||
} from "../../actions/mainActions";
|
||||
import { popPanel, updatePanel } from "../../actions/panelActions";
|
||||
import { finishVideoPreview } from "../../actions/playActions";
|
||||
import {
|
||||
clearProductDetail,
|
||||
getProductOptionId,
|
||||
} from "../../actions/productActions";
|
||||
import TBody from "../../components/TBody/TBody";
|
||||
import TPanel from "../../components/TPanel/TPanel";
|
||||
import { panel_names } from "../../utils/Config";
|
||||
import fp from "../../utils/fp";
|
||||
import { $L, getQRCodeUrl } from "../../utils/helperMethods";
|
||||
import { SpotlightIds } from "../../utils/SpotlightIds";
|
||||
import DetailPanelBackground from "./components/DetailPanelBackground";
|
||||
import THeaderCustom from "./components/THeaderCustom";
|
||||
import css from "./DetailPanel.module.less";
|
||||
import DetailPanelSkeleton from "./DetailPanelSkeleton/DetailPanelSkeleton";
|
||||
import ProductAllSection from "./ProductAllSection/ProductAllSection";
|
||||
import ThemeItemListOverlay from "./ThemeItemListOverlay/ThemeItemListOverlay";
|
||||
finishVideoPreview,
|
||||
pauseFullscreenVideo,
|
||||
resumeFullscreenVideo,
|
||||
} from '../../actions/playActions';
|
||||
import { clearProductDetail, getProductOptionId } from '../../actions/productActions';
|
||||
import TBody from '../../components/TBody/TBody';
|
||||
import TPanel from '../../components/TPanel/TPanel';
|
||||
import { panel_names } from '../../utils/Config';
|
||||
import fp from '../../utils/fp';
|
||||
import { $L, getQRCodeUrl } from '../../utils/helperMethods';
|
||||
import { SpotlightIds } from '../../utils/SpotlightIds';
|
||||
import DetailPanelBackground from './components/DetailPanelBackground';
|
||||
import THeaderCustom from './components/THeaderCustom';
|
||||
import css from './DetailPanel.module.less';
|
||||
import DetailPanelSkeleton from './DetailPanelSkeleton/DetailPanelSkeleton';
|
||||
import ProductAllSection from './ProductAllSection/ProductAllSection';
|
||||
import ThemeItemListOverlay from './ThemeItemListOverlay/ThemeItemListOverlay';
|
||||
|
||||
export default function DetailPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const productData = useSelector((state) => state.main.productData);
|
||||
const youmaylikeData = useSelector((state) => state.main.youmaylikeData);
|
||||
const themeProductInfos = useSelector(
|
||||
(state) => state.home.themeCurationDetailInfoData
|
||||
);
|
||||
const themeProductInfos = useSelector((state) => state.home.themeCurationDetailInfoData);
|
||||
const isLoading = useSelector((state) =>
|
||||
fp.pipe(() => state, fp.get("common.appStatus.showLoadingPanel.show"))()
|
||||
fp.pipe(() => state, fp.get('common.appStatus.showLoadingPanel.show'))()
|
||||
);
|
||||
const themeData = useSelector((state) =>
|
||||
fp.pipe(
|
||||
() => state,
|
||||
fp.get("home.productData.themeInfo"),
|
||||
fp.get('home.productData.themeInfo'),
|
||||
(list) => list && list[0]
|
||||
)()
|
||||
);
|
||||
const webOSVersion = useSelector(
|
||||
(state) => state.common.appStatus.webOSVersion
|
||||
);
|
||||
const webOSVersion = useSelector((state) => state.common.appStatus.webOSVersion);
|
||||
const panels = useSelector((state) => state.panels.panels);
|
||||
|
||||
const [selectedIndex, setSelectedIndex] = useState(0);
|
||||
const localRecentItems = useSelector((state) =>
|
||||
fp.pipe(() => state, fp.get("localSettings.recentItems"))()
|
||||
fp.pipe(() => state, fp.get('localSettings.recentItems'))()
|
||||
);
|
||||
const { httpHeader } = useSelector((state) => state.common);
|
||||
const { popupVisible, activePopup } = useSelector(
|
||||
(state) => state.common.popup
|
||||
);
|
||||
const [lgCatCd, setLgCatCd] = useState("");
|
||||
const { popupVisible, activePopup } = useSelector((state) => state.common.popup);
|
||||
const [lgCatCd, setLgCatCd] = useState('');
|
||||
const [themeProductInfo, setThemeProductInfo] = useState(null);
|
||||
|
||||
const containerRef = useRef(null);
|
||||
|
||||
const panelType = useMemo(
|
||||
() => fp.pipe(() => panelInfo, fp.get("type"))(),
|
||||
[panelInfo]
|
||||
);
|
||||
const panelType = useMemo(() => fp.pipe(() => panelInfo, fp.get('type'))(), [panelInfo]);
|
||||
const panelCurationId = useMemo(
|
||||
() => fp.pipe(() => panelInfo, fp.get("curationId"))(),
|
||||
[panelInfo]
|
||||
);
|
||||
const panelPatnrId = useMemo(
|
||||
() => fp.pipe(() => panelInfo, fp.get("patnrId"))(),
|
||||
[panelInfo]
|
||||
);
|
||||
const panelPrdtId = useMemo(
|
||||
() => fp.pipe(() => panelInfo, fp.get("prdtId"))(),
|
||||
() => fp.pipe(() => panelInfo, fp.get('curationId'))(),
|
||||
[panelInfo]
|
||||
);
|
||||
const panelPatnrId = useMemo(() => fp.pipe(() => panelInfo, fp.get('patnrId'))(), [panelInfo]);
|
||||
const panelPrdtId = useMemo(() => fp.pipe(() => panelInfo, fp.get('prdtId'))(), [panelInfo]);
|
||||
const panelLiveReqFlag = useMemo(
|
||||
() => fp.pipe(() => panelInfo, fp.get("liveReqFlag"))(),
|
||||
[panelInfo]
|
||||
);
|
||||
const panelBgImgNo = useMemo(
|
||||
() => fp.pipe(() => panelInfo, fp.get("bgImgNo"))(),
|
||||
() => fp.pipe(() => panelInfo, fp.get('liveReqFlag'))(),
|
||||
[panelInfo]
|
||||
);
|
||||
const panelBgImgNo = useMemo(() => fp.pipe(() => panelInfo, fp.get('bgImgNo'))(), [panelInfo]);
|
||||
// PlayerPanel에서 진입했는지 여부를 panelInfo에서 추출
|
||||
const panelLaunchedFromPlayer = useMemo(
|
||||
() => fp.pipe(() => panelInfo, fp.get("launchedFromPlayer"))(),
|
||||
() => fp.pipe(() => panelInfo, fp.get('launchedFromPlayer'))(),
|
||||
[panelInfo]
|
||||
);
|
||||
const productPmtSuptYn = useMemo(
|
||||
() => fp.pipe(() => productData, fp.get("pmtSuptYn"))(),
|
||||
() => fp.pipe(() => productData, fp.get('pmtSuptYn'))(),
|
||||
[productData]
|
||||
);
|
||||
const productGrPrdtProcYn = useMemo(
|
||||
() => fp.pipe(() => productData, fp.get("grPrdtProcYn"))(),
|
||||
() => fp.pipe(() => productData, fp.get('grPrdtProcYn'))(),
|
||||
[productData]
|
||||
);
|
||||
|
||||
@@ -118,7 +91,7 @@ export default function DetailPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
() =>
|
||||
fp.pipe(
|
||||
() => panelType,
|
||||
(type) => (type === "theme" ? themeData : productData)
|
||||
(type) => (type === 'theme' ? themeData : productData)
|
||||
)(),
|
||||
[panelType, themeData, productData]
|
||||
);
|
||||
@@ -144,7 +117,7 @@ export default function DetailPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
|
||||
const onSpotlightUpTButton = useCallback((e) => {
|
||||
e.stopPropagation();
|
||||
Spotlight.focus("spotlightId_backBtn");
|
||||
Spotlight.focus('spotlightId_backBtn');
|
||||
}, []);
|
||||
|
||||
const onClick = useCallback(
|
||||
@@ -159,12 +132,12 @@ export default function DetailPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
const shouldUpdatePanel =
|
||||
fp.pipe(
|
||||
() => panels,
|
||||
fp.get("length"),
|
||||
fp.get('length'),
|
||||
(length) => length === 4
|
||||
)() &&
|
||||
fp.pipe(
|
||||
() => panels,
|
||||
fp.get("1.name"),
|
||||
fp.get('1.name'),
|
||||
(name) => name === panel_names.PLAYER_PANEL
|
||||
)();
|
||||
|
||||
@@ -173,7 +146,7 @@ export default function DetailPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
updatePanel({
|
||||
name: panel_names.PLAYER_PANEL,
|
||||
panelInfo: {
|
||||
thumbnail: fp.pipe(() => panelInfo, fp.get("thumbnailUrl"))(),
|
||||
thumbnail: fp.pipe(() => panelInfo, fp.get('thumbnailUrl'))(),
|
||||
},
|
||||
})
|
||||
);
|
||||
@@ -191,31 +164,29 @@ export default function DetailPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
|
||||
const handleScrollToSection = useCallback(
|
||||
(sectionId) => {
|
||||
console.log("DetailPanel: handleScrollToSection called with:", sectionId);
|
||||
console.log("DetailPanel: scrollToSection function:", scrollToSection);
|
||||
console.log('DetailPanel: handleScrollToSection called with:', sectionId);
|
||||
console.log('DetailPanel: scrollToSection function:', scrollToSection);
|
||||
|
||||
const scrollAction = fp.pipe(
|
||||
() => ({ scrollToSection, sectionId }),
|
||||
({ scrollToSection, sectionId }) => {
|
||||
if (fp.isNotNil(scrollToSection)) {
|
||||
return {
|
||||
action: "execute",
|
||||
action: 'execute',
|
||||
scrollFunction: scrollToSection,
|
||||
sectionId,
|
||||
};
|
||||
} else {
|
||||
return { action: "store", sectionId };
|
||||
return { action: 'store', sectionId };
|
||||
}
|
||||
}
|
||||
)();
|
||||
|
||||
// 액션에 따른 처리
|
||||
if (scrollAction.action === "execute") {
|
||||
if (scrollAction.action === 'execute') {
|
||||
scrollAction.scrollFunction(scrollAction.sectionId);
|
||||
} else {
|
||||
console.log(
|
||||
"DetailPanel: scrollToSection function is null, storing pending scroll"
|
||||
);
|
||||
console.log('DetailPanel: scrollToSection function is null, storing pending scroll');
|
||||
setPendingScrollSection(scrollAction.sectionId);
|
||||
}
|
||||
},
|
||||
@@ -233,10 +204,7 @@ export default function DetailPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
)();
|
||||
|
||||
if (shouldExecutePendingScroll) {
|
||||
console.log(
|
||||
"DetailPanel: executing pending scroll to:",
|
||||
pendingScrollSection
|
||||
);
|
||||
console.log('DetailPanel: executing pending scroll to:', pendingScrollSection);
|
||||
|
||||
// 메모리 누수 방지를 위한 cleanup 함수
|
||||
const timeoutId = setTimeout(() => {
|
||||
@@ -262,7 +230,7 @@ export default function DetailPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
},
|
||||
() => {
|
||||
// 테마 데이터 로딩
|
||||
const isThemeType = panelType === "theme";
|
||||
const isThemeType = panelType === 'theme';
|
||||
|
||||
if (isThemeType) {
|
||||
dispatch(
|
||||
@@ -284,7 +252,7 @@ export default function DetailPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
getMainCategoryDetail({
|
||||
patnrId: panelPatnrId,
|
||||
prdtId: panelPrdtId,
|
||||
liveReqFlag: panelLiveReqFlag || "N",
|
||||
liveReqFlag: panelLiveReqFlag || 'N',
|
||||
})
|
||||
);
|
||||
}
|
||||
@@ -315,11 +283,11 @@ export default function DetailPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
exclPatnrId: panelInfo?.patnrId,
|
||||
exclPrdtId: panelInfo?.prdtId,
|
||||
catDpTh3:
|
||||
panelInfo?.type === "theme"
|
||||
panelInfo?.type === 'theme'
|
||||
? themeProductInfos[selectedIndex]?.catDpTh3
|
||||
: productData?.catDpTh3,
|
||||
catDpTh4:
|
||||
panelInfo?.type === "theme"
|
||||
panelInfo?.type === 'theme'
|
||||
? themeProductInfos[selectedIndex]?.catDpTh4
|
||||
: productData?.catDpTh4,
|
||||
};
|
||||
@@ -336,7 +304,7 @@ export default function DetailPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
setLgCatCd(productData.catCd);
|
||||
} else if (
|
||||
themeProductInfos &&
|
||||
themeProductInfos[selectedIndex]?.pmtSuptYn === "N" &&
|
||||
themeProductInfos[selectedIndex]?.pmtSuptYn === 'N' &&
|
||||
panelInfo?.curationId
|
||||
) {
|
||||
const themeCatCd = themeProductInfos[selectedIndex]?.catCd;
|
||||
@@ -350,7 +318,7 @@ export default function DetailPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
// selectedIndex,
|
||||
// themeProductPmtSuptYn: themeProductInfos?.[selectedIndex]?.pmtSuptYn
|
||||
// });
|
||||
setLgCatCd("");
|
||||
setLgCatCd('');
|
||||
}
|
||||
}, [productData, themeProductInfos, selectedIndex, panelInfo?.curationId]);
|
||||
|
||||
@@ -387,7 +355,7 @@ export default function DetailPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
dispatch(clearProductDetail());
|
||||
},
|
||||
() => {
|
||||
setContainerLastFocusedElement(null, ["indicator-GridListContainer"]);
|
||||
setContainerLastFocusedElement(null, ['indicator-GridListContainer']);
|
||||
}
|
||||
)();
|
||||
};
|
||||
@@ -410,9 +378,7 @@ export default function DetailPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
|
||||
const conditionCheckers = useMemo(
|
||||
() => ({
|
||||
hasDataAndCondition: fp.curry(
|
||||
(conditionFn, data) => fp.isNotNil(data) && conditionFn(data)
|
||||
),
|
||||
hasDataAndCondition: fp.curry((conditionFn, data) => fp.isNotNil(data) && conditionFn(data)),
|
||||
equalTo: fp.curry((expected, actual) => actual === expected),
|
||||
checkAllConditions: fp.curry((conditions, data) =>
|
||||
fp.reduce(
|
||||
@@ -443,7 +409,7 @@ export default function DetailPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
// 테마 타입 체크
|
||||
() =>
|
||||
createTypeChecker(
|
||||
"theme",
|
||||
'theme',
|
||||
() =>
|
||||
fp.pipe(
|
||||
() => ({ panelCurationId, themeData }),
|
||||
@@ -453,10 +419,10 @@ export default function DetailPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
() => {
|
||||
const themeProduct = fp.pipe(
|
||||
() => themeData,
|
||||
fp.get("productInfos"),
|
||||
fp.get('productInfos'),
|
||||
fp.get(selectedIndex.toString())
|
||||
)();
|
||||
setProductType("theme");
|
||||
setProductType('theme');
|
||||
setThemeProductInfo(themeProduct);
|
||||
}
|
||||
),
|
||||
@@ -464,7 +430,7 @@ export default function DetailPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
// Buy Now 타입 체크 (curry 활용)
|
||||
() =>
|
||||
createTypeChecker(
|
||||
"buyNow",
|
||||
'buyNow',
|
||||
() =>
|
||||
fp.pipe(
|
||||
() => ({
|
||||
@@ -483,21 +449,21 @@ export default function DetailPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
}) => {
|
||||
const conditions = [
|
||||
() => fp.isNotNil(productData),
|
||||
() => conditionCheckers.equalTo("Y")(productPmtSuptYn),
|
||||
() => conditionCheckers.equalTo("N")(productGrPrdtProcYn),
|
||||
() => conditionCheckers.equalTo('Y')(productPmtSuptYn),
|
||||
() => conditionCheckers.equalTo('N')(productGrPrdtProcYn),
|
||||
() => fp.isNotNil(panelPrdtId),
|
||||
() => versionComparators.isVersionGTE("6.0")(webOSVersion),
|
||||
() => versionComparators.isVersionGTE('6.0')(webOSVersion),
|
||||
];
|
||||
return conditionCheckers.checkAllConditions(conditions)({});
|
||||
}
|
||||
)(),
|
||||
() => setProductType("buyNow")
|
||||
() => setProductType('buyNow')
|
||||
),
|
||||
|
||||
// Shop By Mobile 타입 체크 (curry 활용)
|
||||
() =>
|
||||
createTypeChecker(
|
||||
"shopByMobile",
|
||||
'shopByMobile',
|
||||
() =>
|
||||
fp.pipe(
|
||||
() => ({
|
||||
@@ -516,23 +482,21 @@ export default function DetailPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
}) => {
|
||||
if (!productData) return false;
|
||||
|
||||
const isDirectMobile =
|
||||
conditionCheckers.equalTo("N")(productPmtSuptYn);
|
||||
const isDirectMobile = conditionCheckers.equalTo('N')(productPmtSuptYn);
|
||||
const conditionalMobileConditions = [
|
||||
() => conditionCheckers.equalTo("Y")(productPmtSuptYn),
|
||||
() => conditionCheckers.equalTo("N")(productGrPrdtProcYn),
|
||||
() => versionComparators.isVersionLT("6.0")(webOSVersion),
|
||||
() => conditionCheckers.equalTo('Y')(productPmtSuptYn),
|
||||
() => conditionCheckers.equalTo('N')(productGrPrdtProcYn),
|
||||
() => versionComparators.isVersionLT('6.0')(webOSVersion),
|
||||
() => fp.isNotNil(panelPrdtId),
|
||||
];
|
||||
const isConditionalMobile =
|
||||
conditionCheckers.checkAllConditions(
|
||||
conditionalMobileConditions
|
||||
)({});
|
||||
const isConditionalMobile = conditionCheckers.checkAllConditions(
|
||||
conditionalMobileConditions
|
||||
)({});
|
||||
|
||||
return isDirectMobile || isConditionalMobile;
|
||||
}
|
||||
)(),
|
||||
() => setProductType("shopByMobile")
|
||||
() => setProductType('shopByMobile')
|
||||
),
|
||||
];
|
||||
|
||||
@@ -552,13 +516,7 @@ export default function DetailPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
productGrPrdtProcYn,
|
||||
webOSVersion,
|
||||
}),
|
||||
({
|
||||
productData,
|
||||
panelPrdtId,
|
||||
productPmtSuptYn,
|
||||
productGrPrdtProcYn,
|
||||
webOSVersion,
|
||||
}) => ({
|
||||
({ productData, panelPrdtId, productPmtSuptYn, productGrPrdtProcYn, webOSVersion }) => ({
|
||||
pmtSuptYn: productPmtSuptYn,
|
||||
grPrdtProcYn: productGrPrdtProcYn,
|
||||
prdtId: panelPrdtId,
|
||||
@@ -566,8 +524,8 @@ export default function DetailPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
})
|
||||
)();
|
||||
|
||||
console.warn("Unknown product type:", productData);
|
||||
console.warn("Product data properties:", debugInfo);
|
||||
console.warn('Unknown product type:', productData);
|
||||
console.warn('Product data properties:', debugInfo);
|
||||
}
|
||||
}, [
|
||||
panelCurationId,
|
||||
@@ -583,7 +541,7 @@ export default function DetailPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
|
||||
useEffect(() => {
|
||||
// productData가 로드된 후에만 getProductType 실행
|
||||
if (productData || (panelType === "theme" && themeData)) {
|
||||
if (productData || (panelType === 'theme' && themeData)) {
|
||||
getProductType();
|
||||
}
|
||||
}, [getProductType, productData, themeData, panelType]);
|
||||
@@ -597,7 +555,7 @@ export default function DetailPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
}, [themeData, selectedIndex]);
|
||||
|
||||
const imageUrl = useMemo(
|
||||
() => fp.pipe(() => productData, fp.get("thumbnailUrl960"))(),
|
||||
() => fp.pipe(() => productData, fp.get('thumbnailUrl960'))(),
|
||||
[productData]
|
||||
);
|
||||
|
||||
@@ -611,21 +569,20 @@ export default function DetailPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
() => ({ panelPrdtId, productData }),
|
||||
({ panelPrdtId, productData }) =>
|
||||
fp.isNotNil(panelPrdtId) &&
|
||||
fp.pipe(() => productData, fp.get("prdtNm"), fp.isNotNil)()
|
||||
? fp.pipe(() => productData, fp.get("prdtNm"))()
|
||||
fp.pipe(() => productData, fp.get('prdtNm'), fp.isNotNil)()
|
||||
? fp.pipe(() => productData, fp.get('prdtNm'))()
|
||||
: null
|
||||
)();
|
||||
|
||||
const themeTitle = fp.pipe(
|
||||
() => ({ panelType, themeData }),
|
||||
({ panelType, themeData }) =>
|
||||
panelType === "theme" &&
|
||||
fp.pipe(() => themeData, fp.get("curationNm"), fp.isNotNil)()
|
||||
? fp.pipe(() => themeData, fp.get("curationNm"))()
|
||||
panelType === 'theme' && fp.pipe(() => themeData, fp.get('curationNm'), fp.isNotNil)()
|
||||
? fp.pipe(() => themeData, fp.get('curationNm'))()
|
||||
: null
|
||||
)();
|
||||
|
||||
return productTitle || themeTitle || "";
|
||||
return productTitle || themeTitle || '';
|
||||
}
|
||||
)(),
|
||||
[panelPrdtId, productData, panelType, themeData]
|
||||
@@ -636,10 +593,9 @@ export default function DetailPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
fp.pipe(
|
||||
() => ({ panelPrdtId, productData }),
|
||||
({ panelPrdtId, productData }) =>
|
||||
fp.isNotNil(panelPrdtId) &&
|
||||
fp.pipe(() => productData, fp.get("prdtNm"), fp.isNotNil)()
|
||||
? fp.pipe(() => productData, fp.get("prdtNm"))()
|
||||
: ""
|
||||
fp.isNotNil(panelPrdtId) && fp.pipe(() => productData, fp.get('prdtNm'), fp.isNotNil)()
|
||||
? fp.pipe(() => productData, fp.get('prdtNm'))()
|
||||
: ''
|
||||
)(),
|
||||
[panelPrdtId, productData]
|
||||
);
|
||||
@@ -653,6 +609,49 @@ export default function DetailPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
};
|
||||
}, []);
|
||||
|
||||
// 백그라운드 전체화면 비디오 제어: DetailPanel 진입/퇴장 시
|
||||
useEffect(() => {
|
||||
// console.log('[BgVideo] DetailPanel mounted - checking panels:', {
|
||||
// panelsCount: panels?.length,
|
||||
// panels: panels?.map(p => ({ name: p.name, modal: p.panelInfo?.modal }))
|
||||
// });
|
||||
|
||||
// 전체화면 PlayerPanel(modal=false)이 존재하는지 확인
|
||||
const hasFullscreenPlayerPanel = fp.pipe(
|
||||
() => panels,
|
||||
(panelList) =>
|
||||
panelList.some(
|
||||
(panel) => panel.name === panel_names.PLAYER_PANEL && !panel.panelInfo?.modal
|
||||
)
|
||||
)();
|
||||
|
||||
// ProductAllSection에 비디오가 있는지 확인
|
||||
const hasProductVideo = fp.pipe(() => productData, fp.get('prdtMediaUrl'), fp.isNotNil)();
|
||||
|
||||
// console.log('[BgVideo] hasFullscreenPlayerPanel:', hasFullscreenPlayerPanel);
|
||||
// console.log('[BgVideo] hasProductVideo:', hasProductVideo);
|
||||
|
||||
// 전체화면 PlayerPanel이 있고, 제품에 비디오가 있을 때만 백그라운드 비디오 멈춤
|
||||
if (hasFullscreenPlayerPanel && hasProductVideo) {
|
||||
// console.log('[BgVideo] DetailPanel - Product has video, dispatching pauseFullscreenVideo()');
|
||||
dispatch(pauseFullscreenVideo());
|
||||
} else {
|
||||
console.log('[BgVideo] DetailPanel - Skipping pause:', {
|
||||
reason: !hasFullscreenPlayerPanel ? 'no fullscreen PlayerPanel' : 'no product video',
|
||||
});
|
||||
}
|
||||
|
||||
return () => {
|
||||
// DetailPanel 언마운트 시: 비디오가 있었고 멈췄던 경우만 재생 재개
|
||||
// console.log('[BgVideo] DetailPanel unmounting');
|
||||
if (hasFullscreenPlayerPanel && hasProductVideo) {
|
||||
// console.log('[BgVideo] DetailPanel - Product had video, dispatching resumeFullscreenVideo()');
|
||||
dispatch(resumeFullscreenVideo());
|
||||
}
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []); // 마운트/언마운트 시에만 실행
|
||||
|
||||
return (
|
||||
<div ref={containerRef}>
|
||||
<DetailPanelBackground launchedFromPlayer={panelLaunchedFromPlayer} />
|
||||
@@ -687,12 +686,7 @@ export default function DetailPanel({ panelInfo, isOnTop, spotlightId }) {
|
||||
({ isLoading, panelInfo, productDataSource, productType }) => {
|
||||
const hasRequiredData = fp.pipe(
|
||||
() => [panelInfo, productDataSource, productType],
|
||||
(data) =>
|
||||
fp.reduce(
|
||||
(acc, item) => acc && fp.isNotNil(item),
|
||||
true,
|
||||
data
|
||||
)
|
||||
(data) => fp.reduce((acc, item) => acc && fp.isNotNil(item), true, data)
|
||||
)();
|
||||
|
||||
return {
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
stopAutoClose,
|
||||
resetAutoClose,
|
||||
} from '../../../../actions/videoOverlayActions';
|
||||
import { pauseFullscreenVideo, resumeFullscreenVideo } from '../../../../actions/playActions';
|
||||
import css from './ProductVideo.module.less';
|
||||
|
||||
const SpottableComponent = Spottable('div');
|
||||
@@ -143,7 +144,13 @@ export default function ProductVideoV2({
|
||||
// 썸네일 클릭 핸들러 - 비디오 재생 시작 + Redux dispatch + MediaPlayer 메서드 호출
|
||||
const handleThumbnailClick = useCallback(() => {
|
||||
if (canPlayVideo && !isPlaying) {
|
||||
// console.log('[BgVideo] ProductVideoV2 - Starting video playback');
|
||||
setIsPlaying(true);
|
||||
|
||||
// 백그라운드 전체화면 비디오 일시정지
|
||||
// console.log('[BgVideo] ProductVideoV2 - Pausing background fullscreen video');
|
||||
dispatch(pauseFullscreenVideo());
|
||||
|
||||
// Redux: 오버레이 상태 초기화
|
||||
dispatch(showControls());
|
||||
dispatch(startAutoClose(3000));
|
||||
@@ -157,8 +164,14 @@ export default function ProductVideoV2({
|
||||
|
||||
// 비디오 종료 핸들러 - 썸네일로 복귀 + Redux cleanup + MediaPlayer 메서드 호출
|
||||
const handleVideoEnded = useCallback(() => {
|
||||
// console.log('[BgVideo] ProductVideoV2 - Video ended');
|
||||
setIsPlaying(false);
|
||||
setIsFullscreen(false); // 전체화면도 해제
|
||||
|
||||
// 백그라운드 전체화면 비디오 재생 재개
|
||||
// console.log('[BgVideo] ProductVideoV2 - Resuming background fullscreen video');
|
||||
dispatch(resumeFullscreenVideo());
|
||||
|
||||
// Redux: 오버레이 상태 정리
|
||||
dispatch(stopAutoClose());
|
||||
dispatch(hideControls());
|
||||
@@ -185,12 +198,19 @@ export default function ProductVideoV2({
|
||||
const handleBackButton = useCallback(() => {
|
||||
if (isFullscreen) {
|
||||
// 전체화면이면 일반 모드로
|
||||
// console.log('[BgVideo] ProductVideoV2 - Exiting fullscreen to normal mode');
|
||||
setIsFullscreen(false);
|
||||
dispatch(switchToModal());
|
||||
videoPlayerRef.current?.stopAutoCloseTimeout?.();
|
||||
} else if (isPlaying) {
|
||||
// 일반 모드에서 재생 중이면 썸네일로
|
||||
// console.log('[BgVideo] ProductVideoV2 - Stopping video (back button)');
|
||||
setIsPlaying(false);
|
||||
|
||||
// 백그라운드 전체화면 비디오 재생 재개
|
||||
// console.log('[BgVideo] ProductVideoV2 - Resuming background fullscreen video');
|
||||
dispatch(resumeFullscreenVideo());
|
||||
|
||||
dispatch(stopAutoClose());
|
||||
dispatch(hideControls());
|
||||
// MediaPlayer 직접 제어: autoClose 타이머 중지 및 controls 숨김
|
||||
@@ -202,7 +222,7 @@ export default function ProductVideoV2({
|
||||
// 사용자 활동 감지 시 autoClose 타이머 리셋
|
||||
const handleUserActivity = useCallback(() => {
|
||||
if (isPlaying && !isFullscreen) {
|
||||
console.log('[ProductVideoV2] User activity detected - resetting autoClose timer');
|
||||
// console.log('[ProductVideoV2] User activity detected - resetting autoClose timer');
|
||||
dispatch(resetAutoClose());
|
||||
videoPlayerRef.current?.startAutoCloseTimeout?.();
|
||||
}
|
||||
@@ -268,7 +288,12 @@ export default function ProductVideoV2({
|
||||
useEffect(() => {
|
||||
if (autoPlay && canPlayVideo && !isPlaying) {
|
||||
autoPlayTimerRef.current = setTimeout(() => {
|
||||
// console.log('[BgVideo] ProductVideoV2 - AutoPlay starting');
|
||||
setIsPlaying(true);
|
||||
|
||||
// 백그라운드 전체화면 비디오 일시정지
|
||||
// console.log('[BgVideo] ProductVideoV2 - Pausing background fullscreen video (autoPlay)');
|
||||
dispatch(pauseFullscreenVideo());
|
||||
}, 500);
|
||||
}
|
||||
|
||||
@@ -279,7 +304,7 @@ export default function ProductVideoV2({
|
||||
autoPlayTimerRef.current = null;
|
||||
}
|
||||
};
|
||||
}, [autoPlay, canPlayVideo, isPlaying]);
|
||||
}, [autoPlay, canPlayVideo, isPlaying, dispatch]);
|
||||
|
||||
// VideoPlayer wrapper를 적절한 container로 이동
|
||||
useEffect(() => {
|
||||
@@ -317,14 +342,21 @@ export default function ProductVideoV2({
|
||||
}
|
||||
}, [isFullscreen, isPlaying]);
|
||||
|
||||
// 컴포넌트 언마운트 시 Redux 상태 정리
|
||||
// 컴포넌트 언마운트 시 Redux 상태 정리 및 백그라운드 비디오 재생 재개
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
console.log('[ProductVideoV2] Component unmounting - cleaning up Redux state');
|
||||
// console.log('[BgVideo] ProductVideoV2 - Component unmounting');
|
||||
|
||||
// 비디오가 재생 중이었다면 백그라운드 비디오 재생 재개
|
||||
if (isPlaying) {
|
||||
// console.log('[BgVideo] ProductVideoV2 - Was playing, resuming background video');
|
||||
dispatch(resumeFullscreenVideo());
|
||||
}
|
||||
|
||||
dispatch(stopAutoClose());
|
||||
dispatch(hideControls());
|
||||
};
|
||||
}, [dispatch]);
|
||||
}, [dispatch, isPlaying]);
|
||||
|
||||
if (!canPlayVideo) return null;
|
||||
|
||||
@@ -396,11 +428,9 @@ export default function ProductVideoV2({
|
||||
{typeof window === 'object' && window.PalmSystem && (
|
||||
<source src={productInfo?.prdtMediaUrl} type={videoType} />
|
||||
)}
|
||||
{productInfo?.prdtMediaSubtitlUrl &&
|
||||
typeof window === 'object' &&
|
||||
window.PalmSystem && (
|
||||
<track kind="subtitles" src={productInfo?.prdtMediaSubtitlUrl} default />
|
||||
)}
|
||||
{productInfo?.prdtMediaSubtitlUrl && typeof window === 'object' && window.PalmSystem && (
|
||||
<track kind="subtitles" src={productInfo?.prdtMediaSubtitlUrl} default />
|
||||
)}
|
||||
</VideoPlayer>
|
||||
</div>
|
||||
) : null;
|
||||
|
||||
@@ -356,16 +356,28 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props
|
||||
|
||||
// 새로운 useEffect 추가 (라인 328 이후)
|
||||
useEffect(() => {
|
||||
if (panelInfo?.modal && videoPlayer.current) {
|
||||
if (panelInfo.isPaused) {
|
||||
console.log('[PlayerPanel] Executing pause via videoPlayer.current');
|
||||
// console.log('[BgVideo] PlayerPanel useEffect - panelInfo:', {
|
||||
// modal: panelInfo?.modal,
|
||||
// isPaused: panelInfo?.isPaused,
|
||||
// hasVideoPlayer: !!videoPlayer.current
|
||||
// });
|
||||
|
||||
// modal 여부와 관계없이 videoPlayer가 있고 isPaused 값이 명시적일 때 제어
|
||||
if (videoPlayer.current && panelInfo?.isPaused !== undefined) {
|
||||
if (panelInfo.isPaused === true) {
|
||||
// console.log('[BgVideo] PlayerPanel - Executing pause via videoPlayer.current');
|
||||
videoPlayer.current.pause();
|
||||
} else if (panelInfo.isPaused === false) {
|
||||
console.log('[PlayerPanel] Executing play via videoPlayer.current');
|
||||
// console.log('[BgVideo] PlayerPanel - Executing play via videoPlayer.current');
|
||||
videoPlayer.current.play();
|
||||
}
|
||||
} else {
|
||||
console.log('[BgVideo] PlayerPanel - Skipping video control:', {
|
||||
hasVideoPlayer: !!videoPlayer.current,
|
||||
isPausedValue: panelInfo?.isPaused,
|
||||
});
|
||||
}
|
||||
}, [panelInfo?.isPaused, panelInfo?.modal]);
|
||||
}, [panelInfo?.isPaused]);
|
||||
|
||||
// creating live log params
|
||||
useEffect(() => {
|
||||
|
||||
Reference in New Issue
Block a user