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') {