diff --git a/com.twin.app.shoptime/src/actions/actionTypes.js b/com.twin.app.shoptime/src/actions/actionTypes.js index fa86146f..b2bc63ea 100644 --- a/com.twin.app.shoptime/src/actions/actionTypes.js +++ b/com.twin.app.shoptime/src/actions/actionTypes.js @@ -112,6 +112,7 @@ export const types = { CHECK_ENTER_THROUGH_GNB: 'CHECK_ENTER_THROUGH_GNB', SET_DEFAULT_FOCUS: 'SET_DEFAULT_FOCUS', SET_BANNER_INDEX: 'SET_BANNER_INDEX', + SET_VIDEO_TRANSITION_LOCK: 'SET_VIDEO_TRANSITION_LOCK', RESET_HOME_INFO: 'RESET_HOME_INFO', UPDATE_HOME_INFO: 'UPDATE_HOME_INFO', diff --git a/com.twin.app.shoptime/src/actions/homeActions.js b/com.twin.app.shoptime/src/actions/homeActions.js index d9f02e9f..3c546c30 100644 --- a/com.twin.app.shoptime/src/actions/homeActions.js +++ b/com.twin.app.shoptime/src/actions/homeActions.js @@ -483,6 +483,11 @@ export const setDefaultFocus = (focus) => ({ payload: focus, }); +export const setVideoTransitionLock = (isLocked) => ({ + type: types.SET_VIDEO_TRANSITION_LOCK, + payload: Boolean(isLocked), +}); + export const checkEnterThroughGNB = (boolean) => ({ type: types.CHECK_ENTER_THROUGH_GNB, payload: boolean, diff --git a/com.twin.app.shoptime/src/components/VideoPlayer/TReactPlayer.jsx b/com.twin.app.shoptime/src/components/VideoPlayer/TReactPlayer.jsx index 63e2ecf5..245632bb 100644 --- a/com.twin.app.shoptime/src/components/VideoPlayer/TReactPlayer.jsx +++ b/com.twin.app.shoptime/src/components/VideoPlayer/TReactPlayer.jsx @@ -1,170 +1,168 @@ -import React, { useCallback, useMemo, useRef, useEffect } from "react"; - -import ReactPlayer from "react-player"; - -import handle from "@enact/core/handle"; - -var handledMediaEventsMap = [ - "onReady", - "onStart", - "onPlay", - "onProgress", - "onDuration", - "onPause", - "onBuffer", - "onBufferEnd", - "onSeek", - "onEnded", - "onError", -]; - -export default function TReactPlayer({ - mediaEventsMap = handledMediaEventsMap, - videoRef, - url, - dispatch, - ...rest -}) { - const playerRef = useRef(null); - - const handleEvent = useCallback( - (type) => (ev) => { - if (type === "onReady") { +import React, { useCallback, useMemo, useRef, useEffect } from 'react'; + +import ReactPlayer from 'react-player'; + +import handle from '@enact/core/handle'; + +var handledMediaEventsMap = [ + 'onReady', + 'onStart', + 'onPlay', + 'onProgress', + 'onDuration', + 'onPause', + 'onBuffer', + 'onBufferEnd', + 'onSeek', + 'onEnded', + 'onError', +]; + +export default function TReactPlayer({ + mediaEventsMap = handledMediaEventsMap, + videoRef, + url, + dispatch, + ...rest +}) { + const playerRef = useRef(null); + + const handleEvent = useCallback( + (type) => (ev) => { + if (type === 'onReady') { if (videoRef) { const videoNode = playerRef.current.getInternalPlayer(); videoRef(videoNode); const iframeEl = - typeof playerRef.current?.getInternalPlayer === "function" - ? playerRef.current.getInternalPlayer("iframe") + typeof playerRef.current?.getInternalPlayer === 'function' + ? playerRef.current.getInternalPlayer('iframe') : null; if (iframeEl) { - iframeEl.setAttribute("tabIndex", "-1"); - iframeEl.setAttribute("aria-hidden", "true"); + iframeEl.setAttribute('tabIndex', '-1'); + iframeEl.setAttribute('aria-hidden', 'true'); } if ( videoNode.tagName && - !Object.prototype.hasOwnProperty.call(videoNode, "proportionPlayed") + !Object.prototype.hasOwnProperty.call(videoNode, 'proportionPlayed') ) { - Object.defineProperties(videoNode, { - error: { - get: function () { - return videoNode.networkState === videoNode.NETWORK_NO_SOURCE; - }, - }, - loading: { - get: function () { - return videoNode.readyState < videoNode.HAVE_ENOUGH_DATA; - }, - }, - proportionLoaded: { - get: function () { - return ( - videoNode.buffered.length && - videoNode.buffered.end(videoNode.buffered.length - 1) / - videoNode.duration - ); - }, - }, - proportionPlayed: { - get: function () { - return videoNode.currentTime / videoNode.duration; - }, - }, - }); - } else if ( - !Object.prototype.hasOwnProperty.call(videoNode, "proportionPlayed") - ) { - videoNode.play = videoNode.playVideo; - videoNode.pause = videoNode.pauseVideo; - videoNode.seek = videoNode.seekTo; - Object.defineProperties(videoNode, { - currentTime: { - get: function () { - return videoNode.getCurrentTime(); - }, - set: function (time) { - videoNode.seekTo(time); - }, - }, - duration: { - get: function () { - return videoNode.getDuration(); - }, - }, - paused: { - get: function () { - return videoNode.getPlayerState() !== 1; - }, - }, - error: { - get: function () { - return !!videoNode?.playerInfo?.videoData?.errorCode; - }, - }, - loading: { - get: function () { - return !videoNode?.playerInfo?.videoData?.isPlayable; //todo - }, - }, - proportionLoaded: { - get: function () { - return videoNode?.getVideoBytesLoaded() ?? 0; - }, - }, - proportionPlayed: { - get: function () { - const duration = videoNode.getDuration(); - return duration ? videoNode.getCurrentTime() / duration : 0; - }, - }, - playbackRate: { - get: function () { - return videoNode?.playerInfo?.playbackRate; - }, - set: function (playbackRate) { - if (videoNode && videoNode.setPlaybackRate) { - videoNode.setPlaybackRate(playbackRate); - } - }, - }, - }); - } - } - handle.forward("onLoadStart", { type, ev }, rest); - } - handle.forward("onUpdate", { type, ev }, rest); - }, - [videoRef] - ); - - const handledMediaEvents = useMemo(() => { - const events = {}; - for (let i = 0; i < mediaEventsMap.length; i++) { - const eventName = mediaEventsMap[i]; - events[eventName] = handleEvent(eventName); - } - return events; - }, [handleEvent, mediaEventsMap]); - - useEffect(() => { - return () => { - const videoNode = playerRef.current?.getInternalPlayer(); - if (videoNode && videoNode.pause) { - videoNode.pause(); - videoNode.src = ""; - videoNode.srcObject = null; - } - }; - }, []); - - return ( - - ); -} + Object.defineProperties(videoNode, { + error: { + get: function () { + return videoNode.networkState === videoNode.NETWORK_NO_SOURCE; + }, + }, + loading: { + get: function () { + return videoNode.readyState < videoNode.HAVE_ENOUGH_DATA; + }, + }, + proportionLoaded: { + get: function () { + return ( + videoNode.buffered.length && + videoNode.buffered.end(videoNode.buffered.length - 1) / videoNode.duration + ); + }, + }, + proportionPlayed: { + get: function () { + return videoNode.currentTime / videoNode.duration; + }, + }, + }); + } else if (!Object.prototype.hasOwnProperty.call(videoNode, 'proportionPlayed')) { + videoNode.play = videoNode.playVideo; + videoNode.pause = videoNode.pauseVideo; + videoNode.seek = videoNode.seekTo; + Object.defineProperties(videoNode, { + currentTime: { + get: function () { + return videoNode.getCurrentTime(); + }, + set: function (time) { + videoNode.seekTo(time); + }, + }, + duration: { + get: function () { + return videoNode.getDuration(); + }, + }, + paused: { + get: function () { + return videoNode.getPlayerState() !== 1; + }, + }, + error: { + get: function () { + return !!videoNode?.playerInfo?.videoData?.errorCode; + }, + }, + loading: { + get: function () { + return !videoNode?.playerInfo?.videoData?.isPlayable; //todo + }, + }, + proportionLoaded: { + get: function () { + return videoNode?.getVideoBytesLoaded() ?? 0; + }, + }, + proportionPlayed: { + get: function () { + const duration = videoNode.getDuration(); + return duration ? videoNode.getCurrentTime() / duration : 0; + }, + }, + playbackRate: { + get: function () { + return videoNode?.playerInfo?.playbackRate; + }, + set: function (playbackRate) { + if (videoNode && videoNode.setPlaybackRate) { + videoNode.setPlaybackRate(playbackRate); + } + }, + }, + }); + } + } + handle.forward('onLoadStart', { type, ev }, rest); + } + handle.forward('onUpdate', { type, ev }, rest); + }, + [videoRef] + ); + + const handledMediaEvents = useMemo(() => { + const events = {}; + for (let i = 0; i < mediaEventsMap.length; i++) { + const eventName = mediaEventsMap[i]; + events[eventName] = handleEvent(eventName); + } + return events; + }, [handleEvent, mediaEventsMap]); + + useEffect(() => { + return () => { + const videoNode = playerRef.current?.getInternalPlayer(); + if (videoNode && videoNode.pause) { + videoNode.pause(); + videoNode.src = ''; + videoNode.srcObject = null; + } + }; + }, []); + + return ( + + ); +} diff --git a/com.twin.app.shoptime/src/reducers/homeReducer.js b/com.twin.app.shoptime/src/reducers/homeReducer.js index 1e8a9a9b..ed5d849e 100644 --- a/com.twin.app.shoptime/src/reducers/homeReducer.js +++ b/com.twin.app.shoptime/src/reducers/homeReducer.js @@ -28,6 +28,7 @@ const initialState = { termsIdMap: {}, // added new property to initialState optionalTermsAvailable: false, // 선택약관 존재 여부 persistentVideoInfo: null, // 영구재생 비디오 정보 + videoTransitionLocked: false, }; export const homeReducer = (state = initialState, action) => { @@ -192,6 +193,12 @@ export const homeReducer = (state = initialState, action) => { }; } + case types.SET_VIDEO_TRANSITION_LOCK: + return { + ...state, + videoTransitionLocked: action.payload, + }; + case types.CHECK_ENTER_THROUGH_GNB: { return { ...state, diff --git a/com.twin.app.shoptime/src/views/HomePanel/HomeBanner/RandomUnit.jsx b/com.twin.app.shoptime/src/views/HomePanel/HomeBanner/RandomUnit.jsx index ad92911e..2d103bc5 100644 --- a/com.twin.app.shoptime/src/views/HomePanel/HomeBanner/RandomUnit.jsx +++ b/com.twin.app.shoptime/src/views/HomePanel/HomeBanner/RandomUnit.jsx @@ -15,7 +15,7 @@ import emptyVerImage from '../../../../assets/images/img-home-banner-empty-ver.p import defaultImageItem from '../../../../assets/images/img-thumb-empty-product@3x.png'; import liveShow from '../../../../assets/images/tag-liveshow.png'; import { changeAppStatus } from '../../../actions/commonActions'; -import { updateHomeInfo } from '../../../actions/homeActions'; +import { updateHomeInfo, setVideoTransitionLock } from '../../../actions/homeActions'; import { sendLogTopContents, sendLogTotalRecommend } from '../../../actions/logActions'; import { pushPanel } from '../../../actions/panelActions'; import { @@ -83,8 +83,13 @@ export default function RandomUnit({ const [isFocused, setIsFocused] = useState(false); const [videoError, setVideoError] = useState(false); const [liveIndicies, setLiveIndicies] = useState([]); + const defaultFocus = useSelector((state) => state.home.defaultFocus); + const isVideoTransitionLocked = useSelector((state) => state.home.videoTransitionLocked); const timerRef = useRef(); + const keepTimerOnBlurRef = useRef(false); + const hasAutoPlayStartedRef = useRef(false); + const isDefaultAutoPlayTarget = defaultFocus === spotlightId; const bannerDataRef = useRef(bannerData); const randomDataRef = useRef( bannerDetailInfos && randomNumber !== undefined && bannerDetailInfos.length > 0 @@ -92,12 +97,36 @@ export default function RandomUnit({ : null ); - // ✅ 현재 PlayerPanel의 modal 상태 추적 const playerPanelInfo = useSelector((state) => { const playerPanel = state.panels.panels.find((p) => p.name === panel_names.PLAYER_PANEL); return playerPanel?.panelInfo; }); + const handleStartVideo = useCallback( + (videoProps) => { + dispatch(setVideoTransitionLock(true)); + dispatch(startVideoPlayerNew(videoProps)); + }, + [dispatch] + ); + + const finishAndUnlock = useCallback(() => { + dispatch(finishVideoPreview()); + dispatch(setVideoTransitionLock(false)); + }, [dispatch]); + + useEffect(() => { + if (isVideoTransitionLocked && isCurrentBannerVideoPlaying) { + dispatch(setVideoTransitionLock(false)); + } + }, [dispatch, isCurrentBannerVideoPlaying, isVideoTransitionLocked]); + + useEffect(() => { + if (isVideoTransitionLocked && videoError) { + dispatch(setVideoTransitionLock(false)); + } + }, [dispatch, isVideoTransitionLocked, videoError]); + const topContentsLogInfo = useMemo(() => { if (randomDataRef.current) { const currentRandomData = randomDataRef.current; @@ -403,8 +432,8 @@ export default function RandomUnit({ let action = linkType === 'DSP00507' ? startVideoPlayer : pushPanel; const isNavigatingToDetail = linkInfo.name === panel_names.DETAIL_PANEL; - if (isNavigatingToDetail && playerPanelInfo?.modal !== false && isCurrentBannerVideoPlaying) { - dispatch(finishVideoPreview()); + if (isNavigatingToDetail && playerPanelInfo?.modal !== false) { + finishAndUnlock(); } dispatch(action(linkInfo)); @@ -428,13 +457,13 @@ export default function RandomUnit({ topContentsLogInfo, playerPanelInfo?.modal, sendBannerLog, - isCurrentBannerVideoPlaying, + finishAndUnlock, ]); // 투데이즈딜 클릭 const todayDealClick = useCallback(() => { - if (playerPanelInfo?.modal !== false && isCurrentBannerVideoPlaying) { - dispatch(finishVideoPreview()); + if (playerPanelInfo?.modal !== false) { + finishAndUnlock(); } dispatch( @@ -463,7 +492,7 @@ export default function RandomUnit({ topContentsLogInfo, sendBannerLog, playerPanelInfo?.modal, - isCurrentBannerVideoPlaying, + finishAndUnlock, ]); // 비디오 클릭 @@ -491,20 +520,18 @@ export default function RandomUnit({ playerPanelInfo?.modal ); - dispatch( - startVideoPlayerNew({ - bannerId: spotlightId, - showUrl: randomData.showUrl, - patnrId: randomData.patnrId, - showId: randomData.showId, - shptmBanrTpNm: randomData.showId ? randomData.shptmBanrTpNm : 'MEDIA', - lgCatCd: randomData.lgCatCd, - chanId: randomData.brdcChnlId, - modal: false, - modalContainerId: spotlightId, - modalClassName: css.videoModal, - }) - ); + handleStartVideo({ + bannerId: spotlightId, + showUrl: randomData.showUrl, + patnrId: randomData.patnrId, + showId: randomData.showId, + shptmBanrTpNm: randomData.showId ? randomData.shptmBanrTpNm : 'MEDIA', + lgCatCd: randomData.lgCatCd, + chanId: randomData.brdcChnlId, + modal: false, + modalContainerId: spotlightId, + modalClassName: css.videoModal, + }); dispatch( sendLogTopContents({ @@ -526,6 +553,7 @@ export default function RandomUnit({ onBlur, playerPanelInfo?.modal, dispatch, + handleStartVideo, ]); // 투데이즈 딜 가격 정보 @@ -574,34 +602,55 @@ export default function RandomUnit({ useEffect(() => { if (isFocused && !videoError) { - timerRef.current = setTimeout( - () => - dispatch( - startVideoPlayerNew({ - bannerId: spotlightId, - showUrl: randomData.showUrl, - patnrId: randomData.patnrId, - showId: randomData.showId, - shptmBanrTpNm: randomData.showId ? randomData.shptmBanrTpNm : 'MEDIA', - lgCatCd: randomData.lgCatCd, - chanId: randomData.brdcChnlId, - modal: true, - modalContainerId: spotlightId, - modalClassName: css.videoModal, - isVerticalModal: !isHorizontal, - }) - ), - 1000 - ); + if (timerRef.current) { + clearTimeout(timerRef.current); + } + keepTimerOnBlurRef.current = isDefaultAutoPlayTarget && !hasAutoPlayStartedRef.current; + timerRef.current = setTimeout(() => { + handleStartVideo({ + bannerId: spotlightId, + showUrl: randomData.showUrl, + patnrId: randomData.patnrId, + showId: randomData.showId, + shptmBanrTpNm: randomData.showId ? randomData.shptmBanrTpNm : 'MEDIA', + lgCatCd: randomData.lgCatCd, + chanId: randomData.brdcChnlId, + modal: true, + modalContainerId: spotlightId, + modalClassName: css.videoModal, + isVerticalModal: !isHorizontal, + }); + if (isDefaultAutoPlayTarget) { + hasAutoPlayStartedRef.current = true; + } + keepTimerOnBlurRef.current = false; + timerRef.current = null; + }, 1000); } if (!isFocused) { setVideoError(false); - // dispatch(finishVideoPreview()); + if (timerRef.current && !keepTimerOnBlurRef.current) { + clearTimeout(timerRef.current); + timerRef.current = null; + } } return () => { - clearTimeout(timerRef.current); + if (timerRef.current) { + clearTimeout(timerRef.current); + timerRef.current = null; + } + keepTimerOnBlurRef.current = false; }; - }, [isFocused]); + }, [ + isFocused, + videoError, + isHorizontal, + randomData, + dispatch, + isDefaultAutoPlayTarget, + spotlightId, + handleStartVideo, + ]); useEffect(() => { if (isFocused && broadcast?.type === 'videoError') { diff --git a/com.twin.app.shoptime/src/views/HomePanel/HomePanel.jsx b/com.twin.app.shoptime/src/views/HomePanel/HomePanel.jsx index a2268e33..269b1cb7 100644 --- a/com.twin.app.shoptime/src/views/HomePanel/HomePanel.jsx +++ b/com.twin.app.shoptime/src/views/HomePanel/HomePanel.jsx @@ -1,16 +1,7 @@ -import React, { - useCallback, - useEffect, - useMemo, - useRef, - useState, -} from 'react'; +import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import classNames from 'classnames'; -import { - useDispatch, - useSelector, -} from 'react-redux'; +import { useDispatch, useSelector } from 'react-redux'; import { applyMiddleware } from 'redux'; import Spotlight from '@enact/spotlight'; @@ -33,14 +24,8 @@ import { getHomeMainContents, updateHomeInfo, } from '../../actions/homeActions'; -import { - sendLogGNB, - sendLogTotalRecommend, -} from '../../actions/logActions'; -import { - getSubCategory, - getTop20Show, -} from '../../actions/mainActions'; +import { sendLogGNB, sendLogTotalRecommend } from '../../actions/logActions'; +import { getSubCategory, getTop20Show } from '../../actions/mainActions'; import { getHomeOnSaleInfo } from '../../actions/onSaleActions'; import { updatePanel } from '../../actions/panelActions'; import { @@ -53,8 +38,7 @@ import TBody from '../../components/TBody/TBody'; import TButton, { TYPES } from '../../components/TButton/TButton'; import TPanel from '../../components/TPanel/TPanel'; import TPopUp from '../../components/TPopUp/TPopUp'; -import TVerticalPagenator - from '../../components/TVerticalPagenator/TVerticalPagenator'; +import TVerticalPagenator from '../../components/TVerticalPagenator/TVerticalPagenator'; import useDebugKey from '../../hooks/useDebugKey'; import { useFocusHistory } from '../../hooks/useFocusHistory/useFocusHistory'; import usePrevious from '../../hooks/usePrevious'; @@ -193,6 +177,7 @@ const HomePanel = ({ isOnTop }) => { const focusedContainerIdRef = usePrevious(focusedContainerId); const loadingComplete = useSelector((state) => state.common?.loadingComplete); + const isVideoTransitionLocked = useSelector((state) => state.home.videoTransitionLocked); const onCancel = useCallback(() => { const currentSpot = Spotlight.getCurrent(); @@ -225,7 +210,7 @@ const HomePanel = ({ isOnTop }) => { visible: false, }) ); - }, [dispatch]); + }, [dispatch, isVideoTransitionLocked]); const onClose = useCallback(() => { dispatch(setHidePopup()); @@ -275,7 +260,7 @@ const HomePanel = ({ isOnTop }) => { break; case TEMPLATE_CODE_CONF.PICK_FOR_YOU: nowMenu = LOG_MENU.HOME_PICKED_FOR_YOU; - break; + break; default: nowMenu = LOG_MENU.HOME_TOP; break; @@ -482,69 +467,74 @@ const HomePanel = ({ isOnTop }) => { }, []); // ✅ useCallback: 의존성은 dispatch만 - const _onScroll = useCallback((e) => { - const currentScrollTop = e.scrollTop; - const prevScrollTop = prevScrollTopRef.current; + const _onScroll = useCallback( + (e) => { + const currentScrollTop = e.scrollTop; + const prevScrollTop = prevScrollTopRef.current; - // ✅ 최상단 도달: 1px 비디오 복구 시도 (shouldShrinkRef.current로 읽음) - if (currentScrollTop <= 1) { - if (shouldShrinkRef.current && !expandIntervalRef.current) { - // console.log('[HomePanel] At top (scrollTop <= 1) - starting video expansion'); - expandAttemptRef.current = 0; + // ✅ 최상단 도달: 1px 비디오 복구 시도 (shouldShrinkRef.current로 읽음) + if (currentScrollTop <= 1) { + if (shouldShrinkRef.current && !expandIntervalRef.current) { + // console.log('[HomePanel] At top (scrollTop <= 1) - starting video expansion'); + expandAttemptRef.current = 0; - // Interval 시작: 200ms마다 복구 시도 - expandIntervalRef.current = setInterval(() => { - // 종료 조건: 최대 3회 시도 - if (expandAttemptRef.current >= 3) { - // console.log('[HomePanel] Max expansion attempts reached (3)'); - clearInterval(expandIntervalRef.current); - expandIntervalRef.current = null; - return; - } + // Interval 시작: 200ms마다 복구 시도 + expandIntervalRef.current = setInterval(() => { + // 종료 조건: 최대 3회 시도 + if (expandAttemptRef.current >= 3) { + // console.log('[HomePanel] Max expansion attempts reached (3)'); + clearInterval(expandIntervalRef.current); + expandIntervalRef.current = null; + return; + } - // console.log('[HomePanel] Expansion attempt', expandAttemptRef.current + 1); + // console.log('[HomePanel] Expansion attempt', expandAttemptRef.current + 1); + dispatch(expandVideoFrom1px()); + expandAttemptRef.current++; + }, 200); + } + } + // 최상단 벗어남: interval 정리 + else if (currentScrollTop > 1 && expandIntervalRef.current) { + // console.log('[HomePanel] Left top - clearing expand interval'); + clearInterval(expandIntervalRef.current); + expandIntervalRef.current = null; + } + + // 기존 로직: 아래로 스크롤 + if (currentScrollTop > prevScrollTop) { + // 아래로 스크롤: 비디오를 1px로 축소 + // console.log('[HomePanel] Scrolling down - shrinking video'); + if (!isVideoTransitionLocked) { + dispatch(shrinkVideoTo1px()); + } + // 기존 타이머 취소 + if (scrollExpandTimerRef.current) { + clearTimeout(scrollExpandTimerRef.current); + scrollExpandTimerRef.current = null; + } + } + // 기존 로직: 위로 스크롤 (0이 아닌) + else if (currentScrollTop < prevScrollTop && currentScrollTop > 1) { + // 위로 스크롤 (최상단 아님): 1초 후 복구 + // console.log('[HomePanel] Scrolling up - will expand after 1s'); + // 기존 타이머 취소 + if (scrollExpandTimerRef.current) { + clearTimeout(scrollExpandTimerRef.current); + } + // 1초 후 자동으로 크기 조정 + scrollExpandTimerRef.current = setTimeout(() => { + // console.log('[HomePanel] 1s passed - auto expanding video'); dispatch(expandVideoFrom1px()); - expandAttemptRef.current++; - }, 200); + scrollExpandTimerRef.current = null; + }, 1000); } - } - // 최상단 벗어남: interval 정리 - else if (currentScrollTop > 1 && expandIntervalRef.current) { - // console.log('[HomePanel] Left top - clearing expand interval'); - clearInterval(expandIntervalRef.current); - expandIntervalRef.current = null; - } - // 기존 로직: 아래로 스크롤 - if (currentScrollTop > prevScrollTop) { - // 아래로 스크롤: 비디오를 1px로 축소 - // console.log('[HomePanel] Scrolling down - shrinking video'); - dispatch(shrinkVideoTo1px()); - // 기존 타이머 취소 - if (scrollExpandTimerRef.current) { - clearTimeout(scrollExpandTimerRef.current); - scrollExpandTimerRef.current = null; - } - } - // 기존 로직: 위로 스크롤 (0이 아닌) - else if (currentScrollTop < prevScrollTop && currentScrollTop > 1) { - // 위로 스크롤 (최상단 아님): 1초 후 복구 - // console.log('[HomePanel] Scrolling up - will expand after 1s'); - // 기존 타이머 취소 - if (scrollExpandTimerRef.current) { - clearTimeout(scrollExpandTimerRef.current); - } - // 1초 후 자동으로 크기 조정 - scrollExpandTimerRef.current = setTimeout(() => { - // console.log('[HomePanel] 1s passed - auto expanding video'); - dispatch(expandVideoFrom1px()); - scrollExpandTimerRef.current = null; - }, 1000); - } - - // 이전 scrollTop 업데이트 - prevScrollTopRef.current = currentScrollTop; - }, [dispatch]); + // 이전 scrollTop 업데이트 + prevScrollTopRef.current = currentScrollTop; + }, + [dispatch] + ); const _onFocusedContainerId = useCallback( (containerId) => { @@ -584,7 +574,16 @@ const HomePanel = ({ isOnTop }) => { }, 0); } }, - [panelInfo, firstSpot, enterThroughGNB, defaultFocus, cbChangePageRef, dispatch, isOnTop, panels] + [ + panelInfo, + firstSpot, + enterThroughGNB, + defaultFocus, + cbChangePageRef, + dispatch, + isOnTop, + panels, + ] ); const bestSellerLoaded = useCallback(() => { diff --git a/com.twin.app.shoptime/src/views/MediaPanel/MediaPanel.v3.jsx b/com.twin.app.shoptime/src/views/MediaPanel/MediaPanel.v3.jsx index b4a87efe..e3edabe8 100644 --- a/com.twin.app.shoptime/src/views/MediaPanel/MediaPanel.v3.jsx +++ b/com.twin.app.shoptime/src/views/MediaPanel/MediaPanel.v3.jsx @@ -941,7 +941,7 @@ const MediaPanel = React.forwardRef( // fullscreen 전환 완료 후 플래그 초기화 isTransitioningToFullscreen.current = false; }; - }, [panelInfo?.modal, isOnTop]); + }, [isOnTop]); useEffect(() => { if (showNowInfos && panelInfo.shptmBanrTpNm === 'LIVE') {