From d2c149c914da33719415232f9a3156e0671d2af9 Mon Sep 17 00:00:00 2001
From: optrader
Date: Mon, 24 Nov 2025 10:45:28 +0900
Subject: [PATCH] [251124] fix: PlayerPanel videoState updated
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
๐ ์ปค๋ฐ ์๊ฐ: 2025. 11. 24. 10:45:28
๐ ๋ณ๊ฒฝ ํต๊ณ:
โข ์ด ํ์ผ: 9๊ฐ
โข ์ถ๊ฐ: +509์ค
โข ์ญ์ : -66์ค
๐ ์์ ๋ ํ์ผ:
~ com.twin.app.shoptime/src/actions/mainActions.js
~ com.twin.app.shoptime/src/actions/playActions.js
~ com.twin.app.shoptime/src/api/TAxios.js
~ com.twin.app.shoptime/src/components/VideoPlayer/VideoPlayer.js
~ com.twin.app.shoptime/src/reducers/playReducer.js
~ com.twin.app.shoptime/src/views/HomePanel/HomeBanner/RandomUnit.jsx
~ com.twin.app.shoptime/src/views/HomePanel/HomeBanner/RollingUnit.jsx
~ com.twin.app.shoptime/src/views/HomePanel/HomePanel.jsx
~ com.twin.app.shoptime/src/views/PlayerPanel/PlayerPanel.jsx
๐ง ํจ์ ๋ณ๊ฒฝ ๋ด์ฉ:
๐ com.twin.app.shoptime/src/actions/mainActions.js (javascript):
โ Deleted: onSuccess(), onFail()
๐ com.twin.app.shoptime/src/actions/playActions.js (javascript):
๐ Modified: clearAllVideoTimers(), pauseModalVideo(), hideModalVideo()
โ Deleted: CLEAR_PLAYER_INFO()
๐ com.twin.app.shoptime/src/api/TAxios.js (javascript):
๐ Modified: setTokenRefreshing(), createSafeApiThunk()
๐ com.twin.app.shoptime/src/views/HomePanel/HomeBanner/RandomUnit.jsx (javascript):
๐ Modified: SpotlightContainerDecorator()
๐ com.twin.app.shoptime/src/views/HomePanel/HomeBanner/RollingUnit.jsx (javascript):
๐ Modified: createPanelInfo()
๐ง ์ฃผ์ ๋ณ๊ฒฝ ๋ด์ฉ:
โข ํต์ฌ ๋น์ฆ๋์ค ๋ก์ง ๊ฐ์
โข API ์๋น์ค ๋ ์ด์ด ๊ฐ์
โข UI ์ปดํฌ๋ํธ ์ํคํ
์ฒ ๊ฐ์
---
.../src/actions/mainActions.js | 36 +--
.../src/actions/playActions.js | 109 +++++--
com.twin.app.shoptime/src/api/TAxios.js | 12 +-
.../src/components/VideoPlayer/VideoPlayer.js | 195 +++++++++++-
.../src/reducers/playReducer.js | 80 ++++-
.../views/HomePanel/HomeBanner/RandomUnit.jsx | 6 +-
.../HomePanel/HomeBanner/RollingUnit.jsx | 282 +++++++-----------
.../src/views/HomePanel/HomePanel.jsx | 25 ++
.../src/views/PlayerPanel/PlayerPanel.jsx | 214 ++++++++++++-
9 files changed, 700 insertions(+), 259 deletions(-)
diff --git a/com.twin.app.shoptime/src/actions/mainActions.js b/com.twin.app.shoptime/src/actions/mainActions.js
index 9239497a..5167f09c 100644
--- a/com.twin.app.shoptime/src/actions/mainActions.js
+++ b/com.twin.app.shoptime/src/actions/mainActions.js
@@ -1,5 +1,5 @@
import { URLS } from '../api/apiConfig';
-import { TAxios } from '../api/TAxios';
+import { TAxios, TAxiosAdvancedPromise } from '../api/TAxios';
import { convertUtcToLocal } from '../components/MediaPlayer/util';
import { CATEGORY_DATA_MAX_RESULTS_LIMIT, LOG_CONTEXT_NAME, LOG_MESSAGE_ID } from '../utils/Config';
import * as HelperMethods from '../utils/helperMethods';
@@ -464,29 +464,29 @@ export const getHomeFullVideoInfo =
export const getMainLiveShowNowProduct =
({ patnrId, showId, lstChgDt }) =>
(dispatch, getState) => {
- const onSuccess = (response) => {
- // console.log('getMainLiveShowNowProduct onSuccess', response.data);
-
- dispatch({
- type: types.GET_MAIN_LIVE_SHOW_NOW_PRODUCT,
- payload: response.data.data,
- });
- };
-
- const onFail = (error) => {
- console.error('getMainLiveShowNowProduct onFail', error);
- };
-
- TAxios(
+ return TAxiosAdvancedPromise(
dispatch,
getState,
'get',
URLS.GET_MAIN_LIVE_SHOW_NOW_PRODUCT,
{ patnrId, showId, lstChgDt },
{},
- onSuccess,
- onFail
- );
+ {
+ retries: 2, // 3ํ๊น์ง ์ฌ์๋ (์ฒ์ ์๋ + 2ํ ์ฌ์๋)
+ retryDelay: 500, // 500ms ๊ฐ๊ฒฉ์ผ๋ก ์ฌ์๋
+ throwOnError: false, // ์๋ฌ๋ฅผ throwํ์ง ์๊ณ ๊ฐ์ฒด๋ก ๋ฐํ
+ }
+ ).then((result) => {
+ if (result.success && result.data?.data) {
+ dispatch({
+ type: types.GET_MAIN_LIVE_SHOW_NOW_PRODUCT,
+ payload: result.data.data,
+ });
+ } else {
+ console.error('getMainLiveShowNowProduct onFail', result.error);
+ }
+ return result;
+ });
};
export const clearShopNowInfo = () => {
diff --git a/com.twin.app.shoptime/src/actions/playActions.js b/com.twin.app.shoptime/src/actions/playActions.js
index 9e902a26..ca35d97c 100644
--- a/com.twin.app.shoptime/src/actions/playActions.js
+++ b/com.twin.app.shoptime/src/actions/playActions.js
@@ -68,24 +68,43 @@ export const startVideoPlayer =
...rest
}) =>
(dispatch, getState) => {
- console.log('[startVideoPlayer] โ
START - videoId:', videoId, ', showUrl:', showUrl, ', modal:', modal);
+ console.log(
+ '[startVideoPlayer] โ
START - videoId:',
+ videoId,
+ ', showUrl:',
+ showUrl,
+ ', modal:',
+ modal
+ );
// ๐ฝ [251116] ์ฆ์ ๋ก๋ฉ ์ํ ์ค์
const videoIdentifier = videoId || showUrl;
if (videoIdentifier) {
const displayMode = modal ? DISPLAY_STATUS.VISIBLE : DISPLAY_STATUS.FULLSCREEN;
- console.log('[startVideoPlayer] ๐ Setting playback loading - identifier:', videoIdentifier, ', displayMode:', displayMode);
+ console.log(
+ '[startVideoPlayer] ๐ Setting playback loading - identifier:',
+ videoIdentifier,
+ ', displayMode:',
+ displayMode
+ );
dispatch(setPlaybackLoading(videoIdentifier, displayMode));
} else {
- console.log('[startVideoPlayer] โ ๏ธ No videoIdentifier provided (videoId and showUrl are both missing)');
+ console.log(
+ '[startVideoPlayer] โ ๏ธ No videoIdentifier provided (videoId and showUrl are both missing)'
+ );
}
const panels = getState().panels.panels;
const topPanel = panels[panels.length - 1];
let panelWorkingAction = pushPanel;
-
+
const panelName = panel_names.PLAYER_PANEL;
- console.log('[startVideoPlayer] ๐ Panel state - panelsCount:', panels.length, ', topPanelName:', topPanel?.name);
+ console.log(
+ '[startVideoPlayer] ๐ Panel state - panelsCount:',
+ panels.length,
+ ', topPanelName:',
+ topPanel?.name
+ );
if (topPanel && topPanel.name === panelName) {
panelWorkingAction = updatePanel;
@@ -161,13 +180,27 @@ export const startVideoPlayerNew =
...rest
}) =>
(dispatch, getState) => {
- console.log('[startVideoPlayerNew] *** โ
START - bannerId:', bannerId, ', videoId:', videoId, ', showUrl:', showUrl, ', modal:', modal);
+ console.log(
+ '[startVideoPlayerNew] *** โ
START - bannerId:',
+ bannerId,
+ ', videoId:',
+ videoId,
+ ', showUrl:',
+ showUrl,
+ ', modal:',
+ modal
+ );
// ๐ฝ [251116] ์ฆ์ ๋ก๋ฉ ์ํ ์ค์
const videoIdentifier = videoId || showUrl || bannerId;
if (videoIdentifier) {
const displayMode = modal ? DISPLAY_STATUS.VISIBLE : DISPLAY_STATUS.FULLSCREEN;
- console.log('[startVideoPlayerNew] *** ๐ Setting playback loading - identifier:', videoIdentifier, ', displayMode:', displayMode);
+ console.log(
+ '[startVideoPlayerNew] *** ๐ Setting playback loading - identifier:',
+ videoIdentifier,
+ ', displayMode:',
+ displayMode
+ );
dispatch(setPlaybackLoading(videoIdentifier, displayMode));
} else {
console.log('[startVideoPlayerNew] *** โ ๏ธ No videoIdentifier provided');
@@ -179,11 +212,19 @@ export const startVideoPlayerNew =
// const panelName = useNewPlayer ? panel_names.PLAYER_PANEL_NEW : panel_names.PLAYER_PANEL;
const panelName = panel_names.PLAYER_PANEL;
- console.log('[startVideoPlayerNew] *** ๐ Panel state - panelsCount:', panels.length, ', topPanelName:', topPanel?.name);
+ console.log(
+ '[startVideoPlayerNew] *** ๐ Panel state - panelsCount:',
+ panels.length,
+ ', topPanelName:',
+ topPanel?.name
+ );
if (topPanel && topPanel.name === panelName) {
panelWorkingAction = updatePanel;
- console.log('[startVideoPlayerNew] *** ๐ Current PLAYER_PANEL panelInfo:', topPanel.panelInfo);
+ console.log(
+ '[startVideoPlayerNew] *** ๐ Current PLAYER_PANEL panelInfo:',
+ topPanel.panelInfo
+ );
}
// ์ค๋ณต ์คํ ๋ฐฉ์ง: ๊ฐ์ ๋ฐฐ๋ + ๊ฐ์ modal ์ํ/์ปจํ
์ด๋ + ๊ฐ์ URL์ด๋ฉด skip
@@ -195,7 +236,18 @@ export const startVideoPlayerNew =
const isSameShowUrl = currentPanelInfo.showUrl === showUrl;
const isSameVideoId = currentPanelInfo.videoId === videoId;
- console.log('[startVideoPlayerNew] *** ๐ Duplicate check - isSameBanner:', isSameBanner, ', isSameModalType:', isSameModalType, ', isSameContainer:', isSameContainer, ', isSameShowUrl:', isSameShowUrl, ', isSameVideoId:', isSameVideoId);
+ console.log(
+ '[startVideoPlayerNew] *** ๐ Duplicate check - isSameBanner:',
+ isSameBanner,
+ ', isSameModalType:',
+ isSameModalType,
+ ', isSameContainer:',
+ isSameContainer,
+ ', isSameShowUrl:',
+ isSameShowUrl,
+ ', isSameVideoId:',
+ isSameVideoId
+ );
if (isSameBanner && isSameModalType && isSameContainer && isSameShowUrl && isSameVideoId) {
console.log('[startVideoPlayerNew] *** โญ๏ธ SKIPPED - ๋์ผํ ์์ฒญ', {
@@ -232,7 +284,10 @@ export const startVideoPlayerNew =
true
)
);
- console.log('[startVideoPlayerNew] *** โจ Panel action dispatched - action:', panelWorkingAction === updatePanel ? 'updatePanel' : 'pushPanel');
+ console.log(
+ '[startVideoPlayerNew] *** โจ Panel action dispatched - action:',
+ panelWorkingAction === updatePanel ? 'updatePanel' : 'pushPanel'
+ );
// [COMMENTED OUT] ๋น๋์ค ์ฌ์ ์ ๊ฐ์ ํฌ์ปค์ค ์ด๋ ๋นํ์ฑํ
// if (modal && modalContainerId && !spotlightDisable) {
@@ -324,7 +379,7 @@ export const pauseModalVideo = () => (dispatch, getState) => {
// ๋ชจ๋ฌ ๋น๋์ค๋ฅผ ์ฌ์ (์ผ์์ ์ง ํด์ )
export const resumeModalVideo = () => (dispatch, getState) => {
const panels = getState().panels.panels;
-
+
// modal PlayerPanel ์ฐพ๊ธฐ
const modalPlayerPanel = panels.find(
(panel) => panel.name === panel_names.PLAYER_PANEL && panel.panelInfo?.modal
@@ -498,7 +553,10 @@ export const showModalVideo = () => (dispatch, getState) => {
skipModalStyleRecalculation: false, // ์์น ๋ณ๊ฒฝ ์ DOM ๊ธฐ์ค์ผ๋ก ๋ค์ ๊ณ์ฐํ๋๋ก ํ์ฉ
};
- console.log('[showModalVideo] *** ๐ Updated panelInfo - shouldShrinkTo1px:', updatedPanelInfo.shouldShrinkTo1px);
+ console.log(
+ '[showModalVideo] *** ๐ Updated panelInfo - shouldShrinkTo1px:',
+ updatedPanelInfo.shouldShrinkTo1px
+ );
console.log('[showModalVideo] *** ๐ Restored modalStyle:', updatedPanelInfo.modalStyle);
dispatch(
@@ -699,10 +757,27 @@ export const CLEAR_PLAYER_INFO = () => ({
* @param {number} playState.duration - ์ ์ฒด ๋น๋์ค ๊ธธ์ด(์ด)
* @param {number} playState.playbackRate - ์ฌ์ ์๋
*/
-export const updateVideoPlayState = (playState) => ({
- type: types.UPDATE_VIDEO_PLAY_STATE,
- payload: playState,
-});
+export const updateVideoPlayState = (playState) => (dispatch, getState) => {
+ const currentState = getState().play.videoPlayState;
+
+ // ์ํ ๋ณํ ๊ฐ์ง
+ const hasChanges = Object.keys(playState).some((key) => {
+ return currentState[key] !== playState[key];
+ });
+
+ if (hasChanges) {
+ console.log('๐ [Redux] updateVideoPlayState action created', {
+ ...playState,
+ timestamp: new Date().toISOString(),
+ caller: new Error().stack?.split('\n')[2]?.trim() || 'unknown',
+ });
+ }
+
+ dispatch({
+ type: types.UPDATE_VIDEO_PLAY_STATE,
+ payload: playState,
+ });
+};
/* ๐ฝ [์ถ๊ฐ] ์๋ก์ด 'ํ๋ ์ด ์ ์ด ๋งค๋์ ' ์ก์
๋ค */
diff --git a/com.twin.app.shoptime/src/api/TAxios.js b/com.twin.app.shoptime/src/api/TAxios.js
index 9cda4614..b7b68f47 100644
--- a/com.twin.app.shoptime/src/api/TAxios.js
+++ b/com.twin.app.shoptime/src/api/TAxios.js
@@ -29,7 +29,7 @@ export const setTokenRefreshing = (value) => {
tokenRefreshing = value;
};
export const runDelayedAction = (dispatch, getState) => {
- console.log('runDelayedAction axiosQueue size', axiosQueue.length);
+ // console.log('runDelayedAction axiosQueue size', axiosQueue.length);
while (axiosQueue.length > 0) {
const requestConfig = axiosQueue.shift(); // queue์์ ์์ฒญ์ ํ๋์ฉ shift
TAxios(
@@ -309,7 +309,7 @@ export const TAxiosAdvancedPromise = (
const attemptRequest = () => {
attempts++;
- console.log(`TAxiosPromise attempt ${attempts}/${maxAttempts} for ${baseUrl}`);
+ // console.log(`TAxiosPromise attempt ${attempts}/${maxAttempts} for ${baseUrl}`);
const timeoutId = setTimeout(() => {
const timeoutError = new Error(`Request timeout after ${timeout}ms for ${baseUrl}`);
@@ -335,7 +335,7 @@ export const TAxiosAdvancedPromise = (
// onSuccess
(response) => {
clearTimeout(timeoutId);
- console.log(`TAxiosPromise success on attempt ${attempts} for ${baseUrl}`);
+ // console.log(`TAxiosPromise success on attempt ${attempts} for ${baseUrl}`);
resolve({
success: true,
data: response.data,
@@ -491,7 +491,7 @@ export const safeUsageExamples = {
});
if (result.success) {
- console.log('Success:', result.data);
+ // console.log('Success:', result.data);
return result.data;
} else {
console.error('API call failed:', result.error);
@@ -534,7 +534,7 @@ export const safeUsageExamples = {
const result = await TAxiosAll(requests);
if (result.success) {
- console.log('All requests succeeded');
+ // console.log('All requests succeeded');
return result.successResults.map((item) => item.result);
} else {
console.error('Some requests failed:', result.failedResults);
@@ -562,7 +562,7 @@ export const ComponentUsageExample = () => {
setLoading(false);
if (result.success) {
- console.log('Terms fetched successfully');
+ // console.log('Terms fetched successfully');
// ์ฑ๊ณต ์ฒ๋ฆฌ (์: ์ฑ๊ณต ํ ์คํธ ํ์)
} else {
console.error('Failed to fetch terms:', result.message);
diff --git a/com.twin.app.shoptime/src/components/VideoPlayer/VideoPlayer.js b/com.twin.app.shoptime/src/components/VideoPlayer/VideoPlayer.js
index c03e58ad..ef01c6c9 100644
--- a/com.twin.app.shoptime/src/components/VideoPlayer/VideoPlayer.js
+++ b/com.twin.app.shoptime/src/components/VideoPlayer/VideoPlayer.js
@@ -725,6 +725,7 @@ const VideoPlayerBase = class extends React.Component {
tabContainerVersion: PropTypes.number,
tabIndexV2: PropTypes.number,
dispatch: PropTypes.func,
+ videoPlayState: PropTypes.object,
};
static contextType = FloatingLayerContext;
@@ -1546,17 +1547,101 @@ const VideoPlayerBase = class extends React.Component {
}
this.setState(updatedState);
- // Redux์ ๋น๋์ค ์ฌ์ ์ํ ์
๋ฐ์ดํธ
+ // Redux์ ๋น๋์ค ์ฌ์ ์ํ ์
๋ฐ์ดํธ (๊ธฐ์กด ๋ก์ง ์ ์ง)
if (this.props.dispatch) {
- this.props.dispatch(
- updateVideoPlayState({
+ // ๐ฅ onProgress ์ด๋ฒคํธ๋ Redux ์
๋ฐ์ดํธํ์ง ์์ (๋น๋ฒํ ์ด๋ฒคํธ)
+ const shouldUpdateRedux = !['onProgress'].includes(ev.type);
+
+ if (shouldUpdateRedux) {
+ const updateState = {
isPlaying: !updatedState.paused,
isPaused: updatedState.paused,
currentTime: updatedState.currentTime,
duration: updatedState.duration,
playbackRate: updatedState.playbackRate,
- })
- );
+ };
+
+ // ๊ฐ์ฅ ์ค์ํ ์ด๋ฒคํธ๋ง ๋ก๊ทธ
+ const shouldLogEvent = ['play', 'pause', 'ended'].includes(ev.type);
+ if (shouldLogEvent) {
+ console.log('๐ [PlayerPanel][VideoPlayer] Event-driven Redux update', {
+ eventType: ev.type,
+ videoState: updatedState,
+ updateState,
+ timestamp: new Date().toISOString(),
+ });
+ }
+
+ // ๐ Redux dispatch ํ์ธ
+ console.log('๐ค [PlayerPanel][VideoPlayer] Dispatching Redux update', {
+ eventType: ev.type,
+ updateState,
+ hasDispatch: !!this.props.dispatch,
+ propsVideoPlayState: this.props.videoPlayState,
+ });
+
+ this.props.dispatch(updateVideoPlayState(updateState));
+ }
+ } else {
+ console.log('โ [PlayerPanel][VideoPlayer] No dispatch prop available', {
+ props: Object.keys(this.props),
+ hasDispatch: !!this.props.dispatch,
+ hasVideoPlayState: !!this.props.videoPlayState,
+ });
+ }
+
+ // ๐น [๊ฐํ] ๋ด๋ถ ์ํ์ Redux ์ํ ๋๊ธฐํ
+ // Redux ์ํ๋ฅผ ์ฐ์ ์ ์ผ๋ก ์ฌ์ฉํ์ฌ ๋ด๋ถ ์ํ ์ผ๊ด์ฑ ํ๋ณด
+ if (this.props.videoPlayState && typeof this.props.videoPlayState === 'object') {
+ // Redux ์ํ ๋๋ฒ๊น
(์ต์ํ์ ์ค์ ์ด๋ฒคํธ๋ง)
+ if (ev.type === 'play' || ev.type === 'pause') {
+ console.log('๐ [PlayerPanel][VideoPlayer] Redux state debug', {
+ videoPlayState: this.props.videoPlayState,
+ isPaused: this.props.videoPlayState?.isPaused,
+ isPlaying: this.props.videoPlayState?.isPlaying,
+ currentTime: this.props.videoPlayState?.currentTime,
+ eventType: ev.type,
+ timestamp: new Date().toISOString(),
+ });
+ }
+ const { currentTime, paused, playbackRate } = this.props.videoPlayState;
+
+ // Redux ์ํ์ ํ์ฌ ๋ด๋ถ ์ํ๊ฐ ํฌ๊ฒ ๋ค๋ฅผ ๊ฒฝ์ฐ ๋ด๋ถ ์ํ ์
๋ฐ์ดํธ
+ const timeDiff = Math.abs(currentTime - this.state.currentTime);
+ const shouldUpdateTime = timeDiff > 0.5; // 0.5์ด ์ด์ ์ฐจ์ด ์ ์
๋ฐ์ดํธ
+
+ // ๋น๋ฒํ ์ด๋ฒคํธ๋ ๋ก๊ทธ์์ ์ ์ธ
+ const isFrequentEvent = [
+ 'onProgress',
+ 'onBuffer',
+ 'onBufferEnd',
+ 'onReady',
+ 'onDuration',
+ 'onStart',
+ ].includes(ev.type);
+ const hasSignificantChange =
+ shouldUpdateTime || (paused !== this.state.paused && !isFrequentEvent);
+
+ // ์ค์ํ ์ํ ๋ณํ๊ฐ ์๊ณ ๋น๋ฒํ ์ด๋ฒคํธ๊ฐ ์๋ ๋๋ง ๋ก๊ทธ
+ if (hasSignificantChange && !isFrequentEvent) {
+ console.log('๐ [PlayerPanel][VideoPlayer] Syncing internal state with Redux', {
+ timeDiff,
+ shouldUpdateTime,
+ pausedDiff: paused !== this.state.paused,
+ reduxPaused: paused,
+ internalPaused: this.state.paused,
+ eventType: ev.type,
+ timestamp: new Date().toISOString(),
+ });
+ }
+
+ if (hasSignificantChange) {
+ this.setState({
+ currentTime: shouldUpdateTime ? currentTime : this.state.currentTime,
+ paused: paused !== undefined ? paused : this.state.paused,
+ playbackRate: playbackRate !== undefined ? playbackRate : this.state.playbackRate,
+ });
+ }
}
};
@@ -1569,6 +1654,7 @@ const VideoPlayerBase = class extends React.Component {
/**
* Returns an object with the current state of the media including `currentTime`, `duration`,
* `paused`, `playbackRate`, `proportionLoaded`, and `proportionPlayed`.
+ * Redux ์ํ์ ๋ด๋ถ ์ํ๋ฅผ ์ฐ์ ์ ์ผ๋ก ์ฌ์ฉํ์ฌ ์ผ๊ด์ฑ ๋ณด์ฅ
*
* @function
* @memberof sandstone/VideoPlayer.VideoPlayerBase.prototype
@@ -1576,13 +1662,19 @@ const VideoPlayerBase = class extends React.Component {
* @public
*/
getMediaState = () => {
+ // Redux ์ํ๋ฅผ ์ฐ์ ์ ์ผ๋ก ์ฌ์ฉํ์ฌ ์ผ๊ด์ฑ ๋ณด์ฅ
+ // Redux ์ํ๊ฐ ์์ผ๋ฉด ๋ด๋ถ ์ํ ์ฌ์ฉ (fallback)
+ const reduxState = this.props.videoPlayState;
+
return {
- currentTime: this.state.currentTime,
- duration: this.state.duration,
- paused: this.state.paused,
- playbackRate: this.video?.playbackRate,
+ currentTime: reduxState?.currentTime ?? this.state.currentTime,
+ duration: reduxState?.duration ?? this.state.duration,
+ paused: reduxState?.isPaused ?? this.state.paused,
+ playbackRate: reduxState?.playbackRate ?? this.video?.playbackRate ?? this.state.playbackRate,
proportionLoaded: this.state.proportionLoaded,
proportionPlayed: this.state.proportionPlayed,
+ // Redux ์ํ ์ ๋ณด๋ ํฌํจ
+ isPlaying: reduxState?.isPlaying ?? !this.state.paused,
};
};
@@ -1611,7 +1703,16 @@ const VideoPlayerBase = class extends React.Component {
* @public
*/
play = () => {
+ console.log('๐ข [PlayerPanel][VideoPlayer] play() called', {
+ currentTime: this.state.currentTime,
+ duration: this.state.duration,
+ paused: this.state.paused,
+ sourceUnavailable: this.state.sourceUnavailable,
+ prevCommand: this.prevCommand,
+ });
+
if (this.state.sourceUnavailable) {
+ console.log('โ ๏ธ [PlayerPanel][VideoPlayer] play() aborted - source unavailable');
return;
}
@@ -1623,6 +1724,19 @@ const VideoPlayerBase = class extends React.Component {
this.send('play');
this.announce($L('Play'));
this.startDelayedMiniFeedbackHide(5000);
+
+ // Redux ์ํ ์
๋ฐ์ดํธ - ์ฌ์ ์ํ๋ก ๋ณ๊ฒฝ
+ if (this.props.dispatch) {
+ this.props.dispatch(
+ updateVideoPlayState({
+ isPlaying: true,
+ isPaused: false,
+ currentTime: this.state.currentTime,
+ duration: this.state.duration,
+ playbackRate: 1,
+ })
+ );
+ }
};
/**
@@ -1633,7 +1747,16 @@ const VideoPlayerBase = class extends React.Component {
* @public
*/
pause = () => {
+ console.log('๐ด [VideoPlayer] pause() called', {
+ currentTime: this.state.currentTime,
+ duration: this.state.duration,
+ paused: this.state.paused,
+ sourceUnavailable: this.state.sourceUnavailable,
+ prevCommand: this.prevCommand,
+ });
+
if (this.state.sourceUnavailable) {
+ console.log('โ ๏ธ [VideoPlayer] pause() aborted - source unavailable');
return;
}
@@ -1645,6 +1768,22 @@ const VideoPlayerBase = class extends React.Component {
this.send('pause');
this.announce($L('Pause'));
this.stopDelayedMiniFeedbackHide();
+
+ // Redux ์ํ ์
๋ฐ์ดํธ - ์ผ์์ ์ง ์ํ๋ก ๋ณ๊ฒฝ
+ if (this.props.dispatch) {
+ const pauseState = {
+ isPlaying: false,
+ isPaused: true,
+ currentTime: this.state.currentTime,
+ duration: this.state.duration,
+ playbackRate: 1,
+ };
+
+ console.log('๐ค [VideoPlayer] Dispatching pause state', pauseState);
+ this.props.dispatch(updateVideoPlayState(pauseState));
+ } else {
+ console.log('โ ๏ธ [VideoPlayer] No dispatch prop available - Redux state not updated');
+ }
};
/**
@@ -1656,6 +1795,15 @@ const VideoPlayerBase = class extends React.Component {
* @public
*/
seek = (timeIndex) => {
+ console.log('โฉ [VideoPlayer] seek() called', {
+ timeIndex,
+ currentTime: this.state.currentTime,
+ duration: this.state.duration,
+ videoDuration: this.video?.duration,
+ seekDisabled: this.props.seekDisabled,
+ sourceUnavailable: this.state.sourceUnavailable,
+ });
+
if (this.video) {
if (
!this.props.seekDisabled &&
@@ -1663,14 +1811,37 @@ const VideoPlayerBase = class extends React.Component {
!this.state.sourceUnavailable
) {
// last time error
- if (timeIndex >= this.video.duration) {
- this.video.currentTime = this.video.duration - 1;
+ const actualSeekTime =
+ timeIndex >= this.video.duration ? this.video.duration - 1 : timeIndex;
+ this.video.currentTime = actualSeekTime;
+
+ console.log('โฉ [VideoPlayer] Video seek completed', {
+ requestedTime: timeIndex,
+ actualTime: actualSeekTime,
+ videoDuration: this.video.duration,
+ });
+
+ // Redux ์ํ ์
๋ฐ์ดํธ - ์๊ฐ ์ด๋ ์ํ ๋ฐ์
+ if (this.props.dispatch) {
+ const seekState = {
+ isPlaying: !this.state.paused,
+ isPaused: this.state.paused,
+ currentTime: actualSeekTime,
+ duration: this.state.duration,
+ playbackRate: this.state.playbackRate,
+ };
+
+ console.log('๐ค [VideoPlayer] Dispatching seek state', seekState);
+ this.props.dispatch(updateVideoPlayState(seekState));
} else {
- this.video.currentTime = timeIndex;
+ console.log('โ ๏ธ [VideoPlayer] No dispatch prop available - Redux state not updated');
}
} else {
+ console.log('โ [VideoPlayer] seek failed - disabled or source unavailable');
forward('onSeekFailed', {}, this.props);
}
+ } else {
+ console.log('โ [VideoPlayer] seek failed - no video element');
}
};
diff --git a/com.twin.app.shoptime/src/reducers/playReducer.js b/com.twin.app.shoptime/src/reducers/playReducer.js
index 62e62914..93c6aea7 100644
--- a/com.twin.app.shoptime/src/reducers/playReducer.js
+++ b/com.twin.app.shoptime/src/reducers/playReducer.js
@@ -4,21 +4,52 @@ import { PLAYBACK_STATUS, DISPLAY_STATUS } from '../actions/playActions';
const initialState = {
subTitleBlobs: {},
chatData: null,
- videoPlayState: {
- // ๊ธฐ์กด ์ํ๋ค ์ ์ง
+ // ๐ ํจ๋๋ณ ๋น๋์ค ์ํ ๋ถ๋ฆฌ
+ playerPanelVideoState: {
+ // PlayerPanel ์ ์ฉ ์ํ
isPlaying: false,
isPaused: true,
currentTime: 0,
duration: 0,
playbackRate: 1,
- // ๐ฝ [251116] ์๋ก์ด ๋น๋์ค ์ํ ๊ด๋ฆฌ ์์คํ
- playback: PLAYBACK_STATUS.NOT_PLAYING, // ์ฌ์ ์ํ
- display: DISPLAY_STATUS.HIDDEN, // ํ๋ฉด ํ์ ์ํ
- videoId: null, // ํ์ฌ ๋น๋์ค ID
- loadingProgress: 0, // ๋ก๋ฉ ์งํ๋ฅ (0-100)
- loadingError: null, // ๋ก๋ฉ ์๋ฌ ์ ๋ณด
- lastUpdate: null, // ๋ง์ง๋ง ์
๋ฐ์ดํธ ์๊ฐ
+ // [251116] ์๋ก์ด ๋น๋์ค ์ํ ๊ด๋ฆฌ ์์คํ
+ playback: PLAYBACK_STATUS.NOT_PLAYING,
+ display: DISPLAY_STATUS.HIDDEN,
+ videoId: null,
+ loadingProgress: 0,
+ loadingError: null,
+ lastUpdate: null,
+ },
+ detailPanelVideoState: {
+ // DetailPanel ์ ์ฉ ์ํ
+ isPlaying: false,
+ isPaused: true,
+ currentTime: 0,
+ duration: 0,
+ playbackRate: 1,
+
+ // [251116] ์๋ก์ด ๋น๋์ค ์ํ ๊ด๋ฆฌ ์์คํ
+ playback: PLAYBACK_STATUS.NOT_PLAYING,
+ display: DISPLAY_STATUS.HIDDEN,
+ videoId: null,
+ loadingProgress: 0,
+ loadingError: null,
+ lastUpdate: null,
+ },
+ // ๊ธฐ์กด videoPlayState๋ ํ์ ํธํ์ฑ์ ์ํด ์ ์ง
+ videoPlayState: {
+ isPlaying: false,
+ isPaused: true,
+ currentTime: 0,
+ duration: 0,
+ playbackRate: 1,
+ playback: PLAYBACK_STATUS.NOT_PLAYING,
+ display: DISPLAY_STATUS.HIDDEN,
+ videoId: null,
+ loadingProgress: 0,
+ loadingError: null,
+ lastUpdate: null,
},
};
@@ -60,12 +91,35 @@ export const playReducer = (state = initialState, action) => {
};
}
case types.UPDATE_VIDEO_PLAY_STATE: {
+ const newState = {
+ ...state.videoPlayState,
+ ...action.payload,
+ };
+
+ // ๐ ์ค์ ์ํ ๋ณํ ๊ฐ์ง - ์ค์ํ ๋ณํ๋ง ๋ก๊น
+ const importantKeys = ['isPaused', 'isPlaying', 'playback', 'display'];
+ const hasImportantChange = importantKeys.some((key) => {
+ return state.videoPlayState?.[key] !== newState[key];
+ });
+
+ if (hasImportantChange) {
+ console.log('๐ [Redux Reducer] VIDEO PLAY STATE CRITICAL CHANGE', {
+ previousPaused: state.videoPlayState?.isPaused,
+ newPaused: newState.isPaused,
+ previousPlaying: state.videoPlayState?.isPlaying,
+ newPlaying: newState.isPlaying,
+ previousPlayback: state.videoPlayState?.playback,
+ newPlayback: newState.playback,
+ previousDisplay: state.videoPlayState?.display,
+ newDisplay: newState.display,
+ currentTime: newState.currentTime,
+ timestamp: new Date().toISOString(),
+ });
+ }
+
return {
...state,
- videoPlayState: {
- ...state.videoPlayState,
- ...action.payload,
- },
+ videoPlayState: newState,
};
}
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 5a6396a2..1238ae6e 100644
--- a/com.twin.app.shoptime/src/views/HomePanel/HomeBanner/RandomUnit.jsx
+++ b/com.twin.app.shoptime/src/views/HomePanel/HomeBanner/RandomUnit.jsx
@@ -20,7 +20,7 @@ import liveShow from '../../../../assets/images/tag-liveshow.png';
import { changeAppStatus } from '../../../actions/commonActions';
import { updateHomeInfo, setVideoTransitionLock } from '../../../actions/homeActions';
import { sendLogTopContents, sendLogTotalRecommend } from '../../../actions/logActions';
-import { pushPanel, navigateFromRandomUnit } from '../../../actions/panelActions';
+import { pushPanel, navigateFromRandomUnit, SOURCE_MENUS } from '../../../actions/panelActions';
import {
finishVideoPreview,
startVideoPlayer,
@@ -451,6 +451,8 @@ export default function RandomUnit({
patnrId: randomData.patnrId,
prdtId: randomData.prdtId,
curationId: randomData.lnkCurationId,
+ sourcePanel: panel_names.HOME_PANEL,
+ sourceMenu: SOURCE_MENUS.HOME_RANDOM_UNIT,
},
};
break;
@@ -473,6 +475,8 @@ export default function RandomUnit({
curationId: randomData.lnkCurationId,
prdtId: randomData.prdtId,
type: 'theme',
+ sourcePanel: panel_names.HOME_PANEL,
+ sourceMenu: SOURCE_MENUS.HOME_RANDOM_UNIT,
},
};
break;
diff --git a/com.twin.app.shoptime/src/views/HomePanel/HomeBanner/RollingUnit.jsx b/com.twin.app.shoptime/src/views/HomePanel/HomeBanner/RollingUnit.jsx
index f5884281..64022074 100644
--- a/com.twin.app.shoptime/src/views/HomePanel/HomeBanner/RollingUnit.jsx
+++ b/com.twin.app.shoptime/src/views/HomePanel/HomeBanner/RollingUnit.jsx
@@ -1,42 +1,22 @@
-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 Spotlight from '@enact/spotlight';
-import SpotlightContainerDecorator
- from '@enact/spotlight/SpotlightContainerDecorator';
+import SpotlightContainerDecorator from '@enact/spotlight/SpotlightContainerDecorator';
import Spottable from '@enact/spotlight/Spottable';
import { getContainerId } from '@enact/spotlight/src/container';
import btnPlay from '../../../../assets/images/btn/btn-play-thumb-nor.png';
-import defaultLogoImg
- from '../../../../assets/images/ic-tab-partners-default@3x.png';
-import emptyHorImage
- from '../../../../assets/images/img-home-banner-empty-hor.png';
-import emptyVerImage
- from '../../../../assets/images/img-home-banner-empty-ver.png';
-import defaultImageItem
- from '../../../../assets/images/img-thumb-empty-product@3x.png';
+import defaultLogoImg from '../../../../assets/images/ic-tab-partners-default@3x.png';
+import emptyHorImage from '../../../../assets/images/img-home-banner-empty-hor.png';
+import emptyVerImage from '../../../../assets/images/img-home-banner-empty-ver.png';
+import defaultImageItem from '../../../../assets/images/img-thumb-empty-product@3x.png';
import liveShow from '../../../../assets/images/tag-liveshow.png';
-import {
- setBannerIndex,
- updateHomeInfo,
-} from '../../../actions/homeActions';
-import {
- sendLogTopContents,
- sendLogTotalRecommend,
-} from '../../../actions/logActions';
-import { pushPanel } from '../../../actions/panelActions';
+import { setBannerIndex, updateHomeInfo } from '../../../actions/homeActions';
+import { sendLogTopContents, sendLogTotalRecommend } from '../../../actions/logActions';
+import { pushPanel, SOURCE_MENUS } from '../../../actions/panelActions';
import { startVideoPlayer, finishVideoPreview } from '../../../actions/playActions';
import CustomImage from '../../../components/CustomImage/CustomImage';
import usePriceInfo from '../../../hooks/usePriceInfo';
@@ -47,31 +27,25 @@ import {
LOG_TP_NO,
panel_names,
} from '../../../utils/Config';
-import {
- $L,
- formatGMTString,
-} from '../../../utils/helperMethods';
+import { $L, formatGMTString } from '../../../utils/helperMethods';
import { TEMPLATE_CODE_CONF } from '../HomePanel';
import css from './RollingUnit.module.less';
-const SpottableComponent = Spottable("div");
+const SpottableComponent = Spottable('div');
-const Container = SpotlightContainerDecorator(
- { enterTo: "last-focused", preserveId: true },
- "div"
-);
+const Container = SpotlightContainerDecorator({ enterTo: 'last-focused', preserveId: true }, 'div');
const LINK_TYPES = {
- FEATURED_BRANDS: "DSP00501",
- TRENDING_NOW: "DSP00502",
- HOT_PICKS: "DSP00503",
- ON_SALE: "DSP00504",
- CATEGORY: "DSP00505",
- PRODUCT_DETAIL: "DSP00506",
- VOD: "DSP00507",
- SHOW_DETAIL: "DSP00508",
- THEME: "DSP00509",
- JUSTFORYOU: "DSP00510",
+ FEATURED_BRANDS: 'DSP00501',
+ TRENDING_NOW: 'DSP00502',
+ HOT_PICKS: 'DSP00503',
+ ON_SALE: 'DSP00504',
+ CATEGORY: 'DSP00505',
+ PRODUCT_DETAIL: 'DSP00506',
+ VOD: 'DSP00507',
+ SHOW_DETAIL: 'DSP00508',
+ THEME: 'DSP00509',
+ JUSTFORYOU: 'DSP00510',
};
const createPanelInfo = (data, categoryData = {}) => ({
@@ -101,17 +75,11 @@ export default function RollingUnit({
const { curationId, curationTitle } = useSelector((state) => state.home);
const curtNm = useSelector((state) => state.home?.bannerData?.curtNm);
- const shptmTmplCd = useSelector(
- (state) => state.home?.bannerData?.shptmTmplCd
- );
+ const shptmTmplCd = useSelector((state) => state.home?.bannerData?.shptmTmplCd);
const nowMenu = useSelector((state) => state.common.menu.nowMenu);
const entryMenu = useSelector((state) => state.common.menu.entryMenu);
- const introTermsAgree = useSelector(
- (state) => state.common.optionalTermsAgree
- );
- const homeCategory = useSelector(
- (state) => state.home.menuData?.data?.homeCategory
- );
+ const introTermsAgree = useSelector((state) => state.common.optionalTermsAgree);
+ const homeCategory = useSelector((state) => state.home.menuData?.data?.homeCategory);
const countryCode = useSelector((state) => state.common.httpHeader.cntry_cd);
const { userNumber } = useSelector((state) => state.common.appStatus.loginUserData);
@@ -127,15 +95,15 @@ export default function RollingUnit({
// ์ปดํฌ๋ํธ ์๋จ์์ ํํฐ๋ง
const filteredRollingData = useMemo(() => {
return rollingData.filter(
- (item) => (introTermsAgree === true && (userNumber !== undefined && userNumber !== '')) || item.shptmLnkTpCd !== "DSP00510"
+ (item) =>
+ (introTermsAgree === true && userNumber !== undefined && userNumber !== '') ||
+ item.shptmLnkTpCd !== 'DSP00510'
);
}, [rollingData, introTermsAgree, userNumber]);
// ์ดํ filteredRollingData ์ฌ์ฉ
const rollingDataLength = filteredRollingData.length;
- const [startIndex, setStartIndex] = useState(
- savedIndex !== undefined ? savedIndex : 0
- );
+ const [startIndex, setStartIndex] = useState(savedIndex !== undefined ? savedIndex : 0);
const lastIndexRef = useRef(rollingDataLength - 1);
const doRollingRef = useRef(false);
const [unitHasFocus, setUnitHasFocus] = useState(false);
@@ -157,14 +125,14 @@ export default function RollingUnit({
switch (currentRollingData?.shptmBanrTpCd) {
// case: "LIVE" or "VOD"
- case "DSP00301":
- case "DSP00302":
+ case 'DSP00301':
+ case 'DSP00302':
contId = currentRollingData?.showId;
contNm = currentRollingData?.showNm;
break;
// case: "Image Banner"
- case "DSP00303":
+ case 'DSP00303':
contId = currentRollingData?.shptmLnkTpCd;
contNm = currentRollingData?.shptmLnkTpNm;
break;
@@ -177,27 +145,27 @@ export default function RollingUnit({
}
if (
- currentRollingData?.shptmLnkTpCd === "DSP00503" || // "Hot Picks"
- currentRollingData?.shptmLnkTpCd === "DSP00509" // "Theme"
+ currentRollingData?.shptmLnkTpCd === 'DSP00503' || // "Hot Picks"
+ currentRollingData?.shptmLnkTpCd === 'DSP00509' // "Theme"
) {
- contNm = contNm + " | " + currentRollingData?.lnkCurationId;
+ contNm = contNm + ' | ' + currentRollingData?.lnkCurationId;
}
return {
banrNo: `${currentRollingData?.banrDpOrd}`,
banrTpNm: currentRollingData?.vtctpYn
- ? currentRollingData.vtctpYn === "Y"
- ? "Vertical"
- : "Horizontal"
- : "",
+ ? currentRollingData.vtctpYn === 'Y'
+ ? 'Vertical'
+ : 'Horizontal'
+ : '',
contId,
contNm,
- contTpNm: currentRollingData?.shptmBanrTpNm ?? "",
- dspyTpNm: bannerDataRef.current?.shptmDspyTpNm ?? "",
- expsOrd: bannerDataRef.current?.banrLctnNo ?? "",
- linkTpCd: "",
- patncNm: currentRollingData?.patncNm ?? "",
- patnrId: currentRollingData?.patnrId ?? "",
+ contTpNm: currentRollingData?.shptmBanrTpNm ?? '',
+ dspyTpNm: bannerDataRef.current?.shptmDspyTpNm ?? '',
+ expsOrd: bannerDataRef.current?.banrLctnNo ?? '',
+ linkTpCd: '',
+ patncNm: currentRollingData?.patncNm ?? '',
+ patnrId: currentRollingData?.patnrId ?? '',
tmplCd: shptmTmplCd,
};
}
@@ -208,19 +176,17 @@ export default function RollingUnit({
(bannerClick) => {
const data = rollingDataRef.current[startIndex];
const newParams =
- bannerData.banrLctnNo === "2"
+ bannerData.banrLctnNo === '2'
? {
- bannerType: "Horizontal",
+ bannerType: 'Horizontal',
}
: {
- bannerType: "Vertical",
+ bannerType: 'Vertical',
};
if (rollingDataRef.current && nowMenu === LOG_MENU.HOME_TOP) {
const logParams = {
contextName: LOG_CONTEXT_NAME.HOME,
- messageId: bannerClick
- ? LOG_MESSAGE_ID.BANNER_CLICK
- : LOG_MESSAGE_ID.BANNER,
+ messageId: bannerClick ? LOG_MESSAGE_ID.BANNER_CLICK : LOG_MESSAGE_ID.BANNER,
curationId: curationId,
curationTitle: curationTitle,
contentType: data.shptmBanrTpNm,
@@ -228,7 +194,7 @@ export default function RollingUnit({
contentTitle: data.showNm,
productId: data.prdtId,
productTitle: data.prdtNm,
- displayType: "button",
+ displayType: 'button',
partner: data.patncNm,
brand: data.brndNm,
location: bannerData.banrLctnNo,
@@ -251,16 +217,14 @@ export default function RollingUnit({
const deltaTime = time - previousTimeRef.current;
if (deltaTime >= 10000 && doRollingRef.current) {
- setStartIndex((prevIndex) =>
- prevIndex === lastIndexRef.current ? 0 : prevIndex + 1
- );
+ setStartIndex((prevIndex) => (prevIndex === lastIndexRef.current ? 0 : prevIndex + 1));
previousTimeRef.current = time;
}
} else {
previousTimeRef.current = time;
}
- if (typeof window === "object") {
+ if (typeof window === 'object') {
requestRef.current = window.requestAnimationFrame(animate);
}
}, []);
@@ -298,7 +262,7 @@ export default function RollingUnit({
// ์ธ๋์ผ์ดํฐ ์๋ํค ๋๋ฅผ์ [<]
const prevKeyDown = (event) => {
- if (event.key === "ArrowDown") {
+ if (event.key === 'ArrowDown') {
setNextFocus(true);
setContentsFocus(true);
}
@@ -306,7 +270,7 @@ export default function RollingUnit({
// ์ธ๋์ผ์ดํฐ ์๋ํค ๋๋ฅผ์ [>]
const nextKeyDown = (event) => {
- if (event.key === "ArrowDown") {
+ if (event.key === 'ArrowDown') {
setPrevFocus(true);
setContentsFocus(true);
}
@@ -364,7 +328,16 @@ export default function RollingUnit({
const handlePushPanel = useCallback(
(name, panelInfo) => {
- dispatch(pushPanel({ name, panelInfo }));
+ const isDetailPanel = name === panel_names.DETAIL_PANEL;
+ const enrichedPanelInfo = isDetailPanel
+ ? {
+ sourcePanel: panel_names.HOME_PANEL,
+ sourceMenu: SOURCE_MENUS.HOME_ROLLING_UNIT,
+ ...panelInfo,
+ }
+ : panelInfo;
+
+ dispatch(pushPanel({ name, panelInfo: enrichedPanelInfo }));
},
[dispatch]
);
@@ -390,7 +363,7 @@ export default function RollingUnit({
dispatch(
sendLogTopContents({
...topContentsLogInfo,
- inDt: formatGMTString(new Date()) ?? "",
+ inDt: formatGMTString(new Date()) ?? '',
logTpNo: LOG_TP_NO.TOP_CONTENTS.CLICK,
})
);
@@ -400,26 +373,20 @@ export default function RollingUnit({
switch (linkType) {
case LINK_TYPES.FEATURED_BRANDS:
handlePushPanel(panel_names.FEATURED_BRANDS_PANEL, {
- from: "gnb",
+ from: 'gnb',
patnrId: currentData.patnrId,
});
break;
case LINK_TYPES.TRENDING_NOW:
- handlePushPanel(
- panel_names.TRENDING_NOW_PANEL,
- createPanelInfo(currentData)
- );
+ handlePushPanel(panel_names.TRENDING_NOW_PANEL, createPanelInfo(currentData));
break;
case LINK_TYPES.HOT_PICKS:
if (playerPanelInfo?.modal) {
dispatch(finishVideoPreview());
}
- handlePushPanel(
- panel_names.HOT_PICKS_PANEL,
- createPanelInfo(currentData)
- );
+ handlePushPanel(panel_names.HOT_PICKS_PANEL, createPanelInfo(currentData));
break;
case LINK_TYPES.ON_SALE:
@@ -430,10 +397,7 @@ export default function RollingUnit({
case LINK_TYPES.CATEGORY:
if (Object.keys(categoryData).length > 0) {
- handlePushPanel(
- panel_names.CATEGORY_PANEL,
- createPanelInfo(currentData, categoryData)
- );
+ handlePushPanel(panel_names.CATEGORY_PANEL, createPanelInfo(currentData, categoryData));
}
break;
@@ -454,10 +418,7 @@ export default function RollingUnit({
});
break;
case LINK_TYPES.JUSTFORYOU:
- handlePushPanel(
- panel_names.JUST_FOR_YOU_TEST_PANEL,
- createPanelInfo(currentData)
- );
+ handlePushPanel(panel_names.JUST_FOR_YOU_TEST_PANEL, createPanelInfo(currentData));
break;
default:
return;
@@ -466,7 +427,7 @@ export default function RollingUnit({
dispatch(
sendLogTopContents({
...topContentsLogInfo,
- inDt: formatGMTString(new Date()) ?? "",
+ inDt: formatGMTString(new Date()) ?? '',
logTpNo: LOG_TP_NO.TOP_CONTENTS.CLICK,
})
);
@@ -492,7 +453,7 @@ export default function RollingUnit({
panelInfo: {
lastFocusedTargetId,
focusedContainerId: TEMPLATE_CODE_CONF.TOP,
- currentSpot: currentSpot?.getAttribute("data-spotlight-id"),
+ currentSpot: currentSpot?.getAttribute('data-spotlight-id'),
},
})
);
@@ -508,7 +469,7 @@ export default function RollingUnit({
showUrl: currentData.showUrl,
patnrId: currentData.patnrId,
showId: currentData.showId,
- shptmBanrTpNm: currentData.showId ? currentData.shptmBanrTpNm : "MEDIA",
+ shptmBanrTpNm: currentData.showId ? currentData.shptmBanrTpNm : 'MEDIA',
lgCatCd: currentData.lgCatCd,
chanId: currentData.brdcChnlId,
modal: false,
@@ -520,18 +481,11 @@ export default function RollingUnit({
dispatch(
sendLogTopContents({
...topContentsLogInfo,
- inDt: formatGMTString(new Date()) ?? "",
+ inDt: formatGMTString(new Date()) ?? '',
logTpNo: LOG_TP_NO.TOP_CONTENTS.CLICK,
})
);
- }, [
- rollingData,
- startIndex,
- bannerId,
- dispatch,
- handleStartVideoPlayer,
- topContentsLogInfo,
- ]);
+ }, [rollingData, startIndex, bannerId, dispatch, handleStartVideoPlayer, topContentsLogInfo]);
// 10์ด ๋กค๋ง
useEffect(() => {
@@ -540,20 +494,20 @@ export default function RollingUnit({
previousTimeRef.current = undefined;
if (rollingDataLength <= 1 || unitHasFocus) {
doRollingRef.current = false;
- if (typeof window === "object") {
+ if (typeof window === 'object') {
window.cancelAnimationFrame(requestRef.current);
}
return;
}
doRollingRef.current = true;
- if (typeof window === "object") {
+ if (typeof window === 'object') {
requestRef.current = window.requestAnimationFrame(animate);
}
return () => {
doRollingRef.current = false;
- if (typeof window === "object") {
+ if (typeof window === 'object') {
window.cancelAnimationFrame(requestRef.current);
}
};
@@ -567,7 +521,7 @@ export default function RollingUnit({
const params = {
...topContentsLogInfo,
entryMenu: _entryMenu,
- inDt: formatGMTString(new Date()) ?? "",
+ inDt: formatGMTString(new Date()) ?? '',
logTpNo: LOG_TP_NO.TOP_CONTENTS.VIEW,
nowMenu: _nowMenu,
};
@@ -593,10 +547,7 @@ export default function RollingUnit({
return (
@@ -606,15 +557,14 @@ export default function RollingUnit({
onClick={handlePrev}
onFocus={indicatorFocus}
onBlur={indicatorBlur}
- spotlightId={spotlightId + "Prev"}
+ spotlightId={spotlightId + 'Prev'}
spotlightDisabled={prevFocus}
onKeyDown={prevKeyDown}
aria-label="Move to left Button"
/>
) : null}
- {filteredRollingData &&
- filteredRollingData[startIndex].shptmBanrTpNm === "Image Banner" ? (
+ {filteredRollingData && filteredRollingData[startIndex].shptmBanrTpNm === 'Image Banner' ? (
- ) : filteredRollingData[startIndex].shptmBanrTpNm === "LIVE" ? (
+ ) : filteredRollingData[startIndex].shptmBanrTpNm === 'LIVE' ? (
-
+
-
+
{filteredRollingData[startIndex].tmnlImgPath == null ? (
{filteredRollingData[startIndex].tmnlImgPath == null ? (
- ""
+ ''
) : (
-
+
)}
@@ -700,7 +634,7 @@ export default function RollingUnit({
/>
- ) : filteredRollingData[startIndex].shptmBanrTpNm === "VOD" ? (
+ ) : filteredRollingData[startIndex].shptmBanrTpNm === 'VOD' ? (
-
+
{filteredRollingData[startIndex].tmnlImgPath == null ? (
@@ -736,13 +663,9 @@ export default function RollingUnit({
{filteredRollingData[startIndex].tmnlImgPath == null ? (
- ""
+ ''
) : (
-
+
)}
@@ -764,8 +687,8 @@ export default function RollingUnit({
className={classNames(
css.itemBox,
css.todaysDeals,
- countryCode === "RU" ? css.ru : "",
- countryCode === "DE" ? css.de : "",
+ countryCode === 'RU' ? css.ru : '',
+ countryCode === 'DE' ? css.de : '',
isHorizontal && css.isHorizontal
)}
onClick={imageBannerClick}
@@ -788,7 +711,7 @@ export default function RollingUnit({
}}
/>
- {parseFloat(originalPrice?.replace("$", "")) === 0
+ {parseFloat(originalPrice?.replace('$', '')) === 0
? filteredRollingData[startIndex].offerInfo
: discountRate
? discountedPrice
@@ -797,10 +720,9 @@ export default function RollingUnit({
{originalPrice}
)}
- {isHorizontal &&
- parseFloat(originalPrice?.replace("$", "")) !== 0 && (
-
{originalPrice}
- )}
+ {isHorizontal && parseFloat(originalPrice?.replace('$', '')) !== 0 && (
+
{originalPrice}
+ )}
@@ -822,7 +744,7 @@ export default function RollingUnit({
onClick={handleNext}
onFocus={indicatorFocus}
onBlur={indicatorBlur}
- spotlightId={spotlightId + "Next"}
+ spotlightId={spotlightId + 'Next'}
spotlightDisabled={nextFocus}
onKeyDown={nextKeyDown}
aria-label="Move to right Button"
diff --git a/com.twin.app.shoptime/src/views/HomePanel/HomePanel.jsx b/com.twin.app.shoptime/src/views/HomePanel/HomePanel.jsx
index cc068871..5ef3e078 100644
--- a/com.twin.app.shoptime/src/views/HomePanel/HomePanel.jsx
+++ b/com.twin.app.shoptime/src/views/HomePanel/HomePanel.jsx
@@ -244,6 +244,31 @@ const HomePanel = ({ isOnTop, showGradientBackground = false }) => {
const focusedContainerIdRef = useRef(null);
const prevIsOnTopRef = useRef(isOnTop);
+ // โ
[251124] HomePanel์ ๋ฒ์ด๋ ๋(isOnTop: true -> false) ํ์ฌ ํฌ์ปค์ค ์ ์ฅ
+ useEffect(() => {
+ if (prevIsOnTopRef.current && !isOnTop) {
+ const current = Spotlight.getCurrent();
+ const tBody = document.querySelector(`[data-spotlight-id="${SpotlightIds.HOME_TBODY}"]`);
+
+ if (current && tBody && tBody.contains(current)) {
+ const targetId = current.getAttribute('data-spotlight-id');
+ if (targetId) {
+ dlog('[HomePanel] Saving focus before leaving:', targetId);
+ lastFocusedTargetRef.current = targetId;
+ dispatch(
+ updateHomeInfo({
+ name: panel_names.HOME_PANEL,
+ panelInfo: {
+ lastFocusedTargetId: targetId,
+ },
+ })
+ );
+ }
+ }
+ }
+ prevIsOnTopRef.current = isOnTop;
+ }, [isOnTop, dispatch]);
+
// โ
[251119] DetailPanelBackground ์ด๋ฏธ์ง ํ๋ฆฌ๋ก๋ฉ
// HomePanel ๋ง์ดํธ ์ ๋ฐฑ๊ทธ๋ผ์ด๋๋ก ๋ชจ๋ ํํธ๋์ฌ ๋ฐฐ๊ฒฝ ์ด๋ฏธ์ง๋ฅผ ๋ฏธ๋ฆฌ ๋ก๋ํ์ฌ
// DetailPanel ์ง์
์ ๋ก๋ฉ ์ง์ฐ์ ๋ฐฉ์งํจ
diff --git a/com.twin.app.shoptime/src/views/PlayerPanel/PlayerPanel.jsx b/com.twin.app.shoptime/src/views/PlayerPanel/PlayerPanel.jsx
index f09d05b0..2818d20f 100644
--- a/com.twin.app.shoptime/src/views/PlayerPanel/PlayerPanel.jsx
+++ b/com.twin.app.shoptime/src/views/PlayerPanel/PlayerPanel.jsx
@@ -39,6 +39,7 @@ import {
pauseModalVideo,
resumeModalVideo,
resumeFullscreenVideo,
+ updateVideoPlayState,
} from '../../actions/playActions';
import { resetPlayerOverlays } from '../../actions/videoPlayActions';
import { convertUtcToLocal } from '../../components/MediaPlayer/util';
@@ -436,17 +437,153 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props
}
}, [isOnTop, panelInfo]);
- // ์๋ก์ด useEffect ์ถ๊ฐ (๋ผ์ธ 328 ์ดํ)
+ // ์ด์ ์ํ ์ ์ฅ์ ์ํ ref
+ const previousVideoPlayState = useRef(null);
+ const previousPanelInfo = useRef(null);
+ const previousVideoSource = useRef(null);
+ const previousIsOnTop = useRef(null);
+
+ // Redux ์ํ ๋ณํ ๋ชจ๋ํฐ๋ง useEffect (์ค์ ๋ณํ๋ง)
useEffect(() => {
- // modal ์ฌ๋ถ์ ๊ด๊ณ์์ด videoPlayer๊ฐ ์๊ณ isPaused ๊ฐ์ด ๋ช
์์ ์ผ ๋ ์ ์ด
- if (videoPlayer.current && panelInfo?.isPaused !== undefined) {
- if (panelInfo.isPaused === true) {
- videoPlayer.current.pause();
- } else if (panelInfo.isPaused === false) {
- videoPlayer.current.play();
+ // HomePanel์ด ์ต์์์ด๊ณ videoPlayState๊ฐ ์ค์ ๋ก ๋ณ๊ฒฝ๋์์ ๋๋ง ๋ก๊ทธ
+ const isOnTop = panel_names.HOME_PANEL === topPanel?.name;
+ const hasSignificantChange =
+ previousVideoPlayState.current?.isPlaying !== videoPlayState?.isPlaying ||
+ previousVideoPlayState.current?.isPaused !== videoPlayState?.isPaused ||
+ Math.abs(
+ (previousVideoPlayState.current?.currentTime || 0) - (videoPlayState?.currentTime || 0)
+ ) > 1;
+
+ // ๐ DetailPanel์ผ๋ก ์ธํ ์ผ์์ ์ง ์ํ ๋ณํ๋ ๋ชจ๋ํฐ๋ง
+ const isDetailPanelOnTop = panel_names.DETAIL_PANEL === topPanel?.name;
+ const isPlayingChanged =
+ previousVideoPlayState.current?.isPlaying !== videoPlayState?.isPlaying;
+ const isPausedChanged = previousVideoPlayState.current?.isPaused !== videoPlayState?.isPaused;
+
+ if (
+ (isOnTop && videoPlayState && hasSignificantChange) ||
+ (isDetailPanelOnTop && (isPlayingChanged || isPausedChanged))
+ ) {
+ console.log('๐ [PlayerPanel] Significant videoPlayState change', {
+ previousState: previousVideoPlayState.current,
+ currentState: videoPlayState,
+ topPanelName: topPanel?.name,
+ isOnTop,
+ isDetailPanelOnTop,
+ videoPlayerExists: !!videoPlayer.current,
+ currentPlayingUrl,
+ changeReason: isDetailPanelOnTop ? 'DetailPanel transition' : 'HomePanel state change',
+ // ๐ Redux paused ์ํ ํน๋ณ ํ์ธ
+ reduxIsPaused: videoPlayState?.isPaused,
+ reduxIsPlaying: videoPlayState?.isPlaying,
+ panelInfoIsPaused: panelInfo?.isPaused,
+ timestamp: new Date().toISOString(),
+ source: 'useEffect videoPlayState',
+ });
+ }
+
+ previousVideoPlayState.current = videoPlayState;
+ }, [videoPlayState, topPanel?.name, panelInfo?.isPaused]);
+
+ // PanelInfo ์ํ ๋ณํ ๋ชจ๋ํฐ๋ง useEffect (isPaused๊ฐ ์ค์ ๋ก ๋ณ๊ฒฝ๋ ๋๋ง)
+ useEffect(() => {
+ const isOnTop = panel_names.HOME_PANEL === topPanel?.name;
+ const isPausedChanged = previousPanelInfo.current?.isPaused !== panelInfo?.isPaused;
+
+ if (isOnTop && panelInfo?.isPaused !== undefined && isPausedChanged) {
+ // ์ํ ๋ณ๊ฒฝ ์์๋ง ๋๋ฒ๊น
๋ก๊ทธ ์ถ๋ ฅ
+ console.log('๐ [PlayerPanel] PanelInfo isPaused changed', {
+ previousIsPaused: previousPanelInfo.current?.isPaused,
+ currentIsPaused: panelInfo.isPaused,
+ isOnTop,
+ videoPlayerExists: !!videoPlayer.current,
+ currentPlayingUrl,
+ timestamp: new Date().toISOString(),
+ });
+ console.log('๐ฎ [PlayerPanel] PanelInfo isPaused changed', {
+ previousIsPaused: previousPanelInfo.current?.isPaused,
+ currentIsPaused: panelInfo.isPaused,
+ videoPlayerExists: !!videoPlayer.current,
+ currentPlayingUrl,
+ shptmBanrTpNm: panelInfo?.shptmBanrTpNm,
+ showId: panelInfo?.showId,
+ timestamp: new Date().toISOString(),
+ source: 'useEffect panelInfo.isPaused',
+ });
+
+ if (videoPlayer.current) {
+ if (panelInfo.isPaused === true) {
+ console.log('๐ด [PlayerPanel] Calling VideoPlayer.pause() due to PanelInfo change');
+ videoPlayer.current.pause();
+ } else if (panelInfo.isPaused === false) {
+ console.log('๐ข [PlayerPanel] Calling VideoPlayer.play() due to PanelInfo change');
+ videoPlayer.current.play();
+ }
}
}
- }, [panelInfo?.isPaused]);
+
+ previousPanelInfo.current = panelInfo;
+ }, [panelInfo?.isPaused, topPanel?.name, currentPlayingUrl]);
+
+ // VideoPlayer ์ธ์คํด์ค ๋ฐ ์์ค ๋ณ๊ฒฝ ๋ชจ๋ํฐ๋ง (์ค์ ๋ณํ๋ง)
+ useEffect(() => {
+ const isOnTop = panel_names.HOME_PANEL === topPanel?.name;
+ const isDetailPanelOnTop = panel_names.DETAIL_PANEL === topPanel?.name;
+ const isVideoSourceChanged = previousVideoSource.current !== currentPlayingUrl;
+ const isOnTopChanged = previousIsOnTop.current !== isOnTop;
+ const isDetailPanelOnTopChanged =
+ previousIsOnTop.current === false && isDetailPanelOnTop === true;
+ const videoPlayerJustCreated = previousVideoSource.current === null && videoPlayer.current;
+
+ if (
+ (isVideoSourceChanged ||
+ isOnTopChanged ||
+ isDetailPanelOnTopChanged ||
+ videoPlayerJustCreated) &&
+ videoPlayer.current
+ ) {
+ const changeReason = isDetailPanelOnTopChanged
+ ? 'DetailPanel opened'
+ : isVideoSourceChanged
+ ? 'Video source changed'
+ : isOnTopChanged
+ ? 'Top panel changed'
+ : 'VideoPlayer created';
+
+ console.log('๐ฌ [PlayerPanel] VideoPlayer state change', {
+ hasVideoPlayer: !!videoPlayer.current,
+ currentPlayingUrl,
+ previousVideoSource: previousVideoSource.current,
+ topPanelName: topPanel?.name,
+ isOnTop,
+ isDetailPanelOnTop,
+ isVideoSourceChanged,
+ isOnTopChanged,
+ isDetailPanelOnTopChanged,
+ videoPlayerJustCreated,
+ changeReason,
+ timestamp: new Date().toISOString(),
+ source: 'useEffect videoPlayer.current',
+ });
+
+ // VideoPlayer ์ํ ํ์ธ (์์ค ๋ณ๊ฒฝ์ด๋ PlayerPanel ์์ฑ ์์๋ง)
+ if (isVideoSourceChanged || videoPlayerJustCreated || isDetailPanelOnTopChanged) {
+ const mediaState = videoPlayer.current.getMediaState?.();
+ if (mediaState) {
+ console.log('๐ [PlayerPanel] VideoPlayer current state', {
+ mediaState,
+ videoPlayState,
+ changeReason,
+ timestamp: new Date().toISOString(),
+ source: 'useEffect getMediaState',
+ });
+ }
+ }
+ }
+
+ previousVideoSource.current = currentPlayingUrl;
+ previousIsOnTop.current = isOnTop || isDetailPanelOnTop; // ํ์ฌ ์ต์์ ํจ๋ ์ํ ์ ์ฅ
+ }, [videoPlayer.current, currentPlayingUrl, topPanel?.name]);
// Modal ์ํ ๋ณํ ๊ฐ์ง (true โ false, false โ true)
useEffect(() => {
@@ -1130,11 +1267,63 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props
// ์ต์๋จ ํจ๋์ด DetailPanel์ด๊ณ PlayerPanel์์ ์ง์
ํ๋์ง ํ์ธ
const isTopPanelDetailFromPlayer = useMemo(() => {
- return (
+ const result =
topPanel?.name === panel_names.DETAIL_PANEL &&
- topPanel?.panelInfo?.launchedFromPlayer === true
- );
- }, [topPanel]);
+ topPanel?.panelInfo?.launchedFromPlayer === true;
+
+ // ๐ DetailPanel ์ํ ๋ณํ ๋ก๊น
+ if (result) {
+ console.log('๐ฌ [PlayerPanel] DetailPanel is now on top (from Player)', {
+ topPanelName: topPanel?.name,
+ launchedFromPlayer: topPanel?.panelInfo?.launchedFromPlayer,
+ modalPlayerPanelExists: panels.some(
+ (p) => p.name === panel_names.PLAYER_PANEL && p.panelInfo?.modal
+ ),
+ currentVideoState: {
+ isPlaying: videoPlayState?.isPlaying,
+ isPaused: videoPlayState?.isPaused,
+ currentTime: videoPlayState?.currentTime,
+ },
+ timestamp: new Date().toISOString(),
+ });
+ }
+
+ return result;
+ }, [topPanel, panels, videoPlayState]);
+
+ // ๐ PlayerPanel์ด ๋ฐ์ ๊น๋ ธ์ ๋ ์์ ์ ์ํ๋ฅผ ์
๋ฐ์ดํธํ๋ useEffect
+ useEffect(() => {
+ const isDetailPanelOnTop = topPanel?.name === panel_names.DETAIL_PANEL;
+ const isModalPlayerPanel = panelInfo?.modal === true;
+ const isCurrentPanelOnTop = topPanel?.name === panel_names.PLAYER_PANEL;
+
+ // PlayerPanel์ด modal์ด๊ณ ๋ฐ์ ๊น๋ ธ์ ๋
+ if (isModalPlayerPanel && !isCurrentPanelOnTop && isDetailPanelOnTop) {
+ console.log('๐ด [PlayerPanel] Self-pausing due to DetailPanel on top', {
+ isDetailPanelOnTop,
+ isModalPlayerPanel,
+ isCurrentPanelOnTop,
+ currentReduxState: videoPlayState,
+ needsPause: videoPlayState?.isPlaying === true || videoPlayState?.isPaused === false,
+ timestamp: new Date().toISOString(),
+ });
+
+ // PlayerPanel ์์ ์ ์ํ๋ฅผ ์ผ์์ ์ง๋ก ์
๋ฐ์ดํธ
+ if (videoPlayState?.isPlaying === true || videoPlayState?.isPaused === false) {
+ console.log('๐ [PlayerPanel] Dispatching self-pause to Redux');
+ dispatch(
+ updateVideoPlayState({
+ isPlaying: false,
+ isPaused: true,
+ currentTime: videoPlayState?.currentTime || 0,
+ duration: videoPlayState?.duration || 0,
+ playbackRate: videoPlayState?.playbackRate || 1,
+ source: 'PlayerPanel-self-pause',
+ })
+ );
+ }
+ }
+ }, [topPanel?.name, panelInfo?.modal, videoPlayState, dispatch]);
const cannotPlay = useMemo(() => {
return !isOnTop && topPanel?.name === panel_names.PLAYER_PANEL;
@@ -2624,6 +2813,7 @@ const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props
tabContainerVersion={tabContainerVersion}
tabIndexV2={tabIndexV2}
dispatch={dispatch}
+ videoPlayState={videoPlayState}
>
{typeof window === 'object' && window.PalmSystem && (