[251116] feat: PlayerPanel FullScreen

🕐 커밋 시간: 2025. 11. 16. 21:32:12

📊 변경 통계:
  • 총 파일: 5개
  • 추가: +81줄
  • 삭제: -20줄

📝 수정된 파일:
  ~ com.twin.app.shoptime/src/actions/playActions.js
  ~ com.twin.app.shoptime/src/hooks/useVideoPlay/useVideoPlay.js
  ~ com.twin.app.shoptime/src/middleware/panelHistoryMiddleware.js
  ~ com.twin.app.shoptime/src/views/HomePanel/HomePanel.jsx
  ~ com.twin.app.shoptime/src/views/MainView/MainView.jsx

🔧 함수 변경 내용:
  📄 com.twin.app.shoptime/src/actions/playActions.js (javascript):
    🔄 Modified: resumeFullscreenVideo()

🔧 주요 변경 내용:
  • 핵심 비즈니스 로직 개선
This commit is contained in:
2025-11-16 21:32:12 +09:00
parent da1a050a10
commit a18c61380c
5 changed files with 197 additions and 57 deletions

View File

@@ -161,11 +161,18 @@ export const startVideoPlayerNew =
panelWorkingAction = updatePanel;
}
// 중복 실행 방지: 현재 PlayerPanel이 같은 배너를 재생 중이면 skip
const currentPlayerState = topPanel?.panelInfo?.playerState || {};
if (currentPlayerState?.currentBannerId === bannerId) {
console.log('[playActions] startVideoPlayerNew: 동일 배너 재생 중이라 중복 실행 SKIP', {
// 중복 실행 방지: 같은 배너 + 같은 modal 상태/컨테이너면 skip
const currentPanelInfo = topPanel?.panelInfo || {};
const currentPlayerState = currentPanelInfo.playerState || {};
const isSameBanner = currentPlayerState.currentBannerId === bannerId;
const isSameModalType = currentPanelInfo.modal === modal;
const isSameContainer = currentPanelInfo.modalContainerId === modalContainerId;
if (isSameBanner && isSameModalType && isSameContainer) {
console.log('[playActions] startVideoPlayerNew: 동일한 요청이므로 스킵', {
bannerId,
modal,
modalContainerId,
});
return;
}
@@ -397,7 +404,13 @@ export const shrinkVideoTo1px = () => (dispatch, getState) => {
})
);
} else {
console.log('[HomePanel] shrinkVideoTo1px: No modal PlayerPanel found');
console.log('[HomePanel] shrinkVideoTo1px: No modal PlayerPanel found', {
panels: panels.map((p) => ({
name: p.name,
modal: p.panelInfo?.modal,
shouldShrinkTo1px: p.panelInfo?.shouldShrinkTo1px,
})),
});
}
};
@@ -445,7 +458,13 @@ export const expandVideoFrom1px = () => (dispatch, getState) => {
})
);
} else {
console.log('[HomePanel] expandVideoFrom1px: No shrunk modal PlayerPanel found');
console.log('[HomePanel] expandVideoFrom1px: No shrunk modal PlayerPanel found', {
panels: panels.map((p) => ({
name: p.name,
modal: p.panelInfo?.modal,
shouldShrinkTo1px: p.panelInfo?.shouldShrinkTo1px,
})),
});
}
};

View File

@@ -3,7 +3,7 @@
import { useCallback, useRef, useState, useEffect, useMemo } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import {
startBannerVideo,
startVideoPlayerNew,
stopBannerVideo,
stopAndHideVideo,
hidePlayerVideo,
@@ -143,7 +143,14 @@ export const useVideoPlay = (options = {}) => {
videoState.setCurrentPlaying(bannerId);
// Redux 액션 dispatch - bannerId를 modalContainerId로 사용
dispatch(startBannerVideo(bannerId, { modalContainerId: bannerId, force }));
dispatch(
startVideoPlayerNew({
bannerId,
modal: true,
modalContainerId: bannerId,
force,
})
);
// 성공 상태 업데이트
setErrorCount(0); // 성공 시 오류 카운트 초기화

View File

@@ -22,10 +22,11 @@ const DEBUG_MODE = true;
*/
export const panelHistoryMiddleware = (store) => (next) => (action) => {
// 모든 PANEL 관련 액션 로깅
if (DEBUG_MODE && action.type && (
action.type.includes('PANEL') ||
action.type === 'CLEAR_PANEL_HISTORY'
)) {
if (
DEBUG_MODE &&
action.type &&
(action.type.includes('PANEL') || action.type === 'CLEAR_PANEL_HISTORY')
) {
const caller = new Error().stack.split('\n')[1]?.trim();
console.log(`[PANEL DEBUG] ${action.type} from: ${caller}`);
console.log(' Payload:', action.payload);
@@ -38,8 +39,7 @@ export const panelHistoryMiddleware = (store) => (next) => (action) => {
const stackLines = stack.split('\n');
for (const line of stackLines) {
if (line.includes('TabLayout.jsx') ||
line.includes('TIconButton.jsx')) {
if (line.includes('TabLayout.jsx') || line.includes('TIconButton.jsx')) {
return true;
}
}
@@ -75,15 +75,36 @@ export const panelHistoryMiddleware = (store) => (next) => (action) => {
if (panelName) {
const isGNB = isGNBCall();
const isOnTop = calculateIsOnTop(panelName); // 🎯 isOnTop 계산
if (DEBUG_MODE) console.log('[PANEL] PUSH_PANEL:', { panelName, panelInfo, isGNB, isOnTop, timestamp: new Date().toISOString() });
store.dispatch(enqueuePanelHistory(panelName, panelInfo, 'PUSH', new Date().toISOString(), isGNB, false, isOnTop));
if (DEBUG_MODE)
console.log('[PANEL] PUSH_PANEL:', {
panelName,
panelInfo,
isGNB,
isOnTop,
timestamp: new Date().toISOString(),
});
store.dispatch(
enqueuePanelHistory(
panelName,
panelInfo,
'PUSH',
new Date().toISOString(),
isGNB,
false,
isOnTop
)
);
// PanelHistory 상태 로그 (state 업데이트 후)
const logPanelHistoryAfter = () => {
if (DEBUG_MODE) {
const stateAfter = store.getState();
const panelHistoryAfter = stateAfter.panelHistory;
console.log('[PANEL_HISTORY] After PUSH_PANEL:', panelHistoryAfter);
const panelsAfter = stateAfter.panels.panels;
console.log('[PANEL_HISTORY] After PUSH_PANEL:', {
panelHistory: panelHistoryAfter,
panels: panelsAfter,
});
}
};
@@ -102,15 +123,36 @@ export const panelHistoryMiddleware = (store) => (next) => (action) => {
if (topPanel && topPanel.name) {
const isGNB = isGNBCall();
const isOnTop = calculateIsOnTop(topPanel.name); // 🎯 isOnTop 계산
if (DEBUG_MODE) console.log('[PANEL] POP_PANEL:', { panelName: topPanel.name, panelInfo: topPanel.panelInfo || {}, isGNB, isOnTop, timestamp: new Date().toISOString() });
store.dispatch(enqueuePanelHistory(topPanel.name, topPanel.panelInfo || {}, 'POP', new Date().toISOString(), isGNB, false, isOnTop));
if (DEBUG_MODE)
console.log('[PANEL] POP_PANEL:', {
panelName: topPanel.name,
panelInfo: topPanel.panelInfo || {},
isGNB,
isOnTop,
timestamp: new Date().toISOString(),
});
store.dispatch(
enqueuePanelHistory(
topPanel.name,
topPanel.panelInfo || {},
'POP',
new Date().toISOString(),
isGNB,
false,
isOnTop
)
);
// PanelHistory 상태 로그 (state 업데이트 후)
const logPanelHistoryAfter = () => {
if (DEBUG_MODE) {
const stateAfter = store.getState();
const panelHistoryAfter = stateAfter.panelHistory;
console.log('[PANEL_HISTORY] After POP_PANEL:', panelHistoryAfter);
const panelsAfter = stateAfter.panels.panels;
console.log('[PANEL_HISTORY] After POP_PANEL:', {
panelHistory: panelHistoryAfter,
panels: panelsAfter,
});
}
};
@@ -129,15 +171,36 @@ export const panelHistoryMiddleware = (store) => (next) => (action) => {
if (panelName) {
const isGNB = isGNBCall();
const isOnTop = calculateIsOnTop(panelName); // 🎯 isOnTop 계산
if (DEBUG_MODE) console.log('[PANEL] UPDATE_PANEL:', { panelName, panelInfo, isGNB, isOnTop, timestamp: new Date().toISOString() });
store.dispatch(enqueuePanelHistory(panelName, panelInfo, 'UPDATE', new Date().toISOString(), isGNB, false, isOnTop));
if (DEBUG_MODE)
console.log('[PANEL] UPDATE_PANEL:', {
panelName,
panelInfo,
isGNB,
isOnTop,
timestamp: new Date().toISOString(),
});
store.dispatch(
enqueuePanelHistory(
panelName,
panelInfo,
'UPDATE',
new Date().toISOString(),
isGNB,
false,
isOnTop
)
);
// PanelHistory 상태 로그 (state 업데이트 후)
const logPanelHistoryAfter = () => {
if (DEBUG_MODE) {
const stateAfter = store.getState();
const panelHistoryAfter = stateAfter.panelHistory;
console.log('[PANEL_HISTORY] After UPDATE_PANEL:', panelHistoryAfter);
const panelsAfter = stateAfter.panels.panels;
console.log('[PANEL_HISTORY] After UPDATE_PANEL:', {
panelHistory: panelHistoryAfter,
panels: panelsAfter,
});
}
};
@@ -150,11 +213,13 @@ export const panelHistoryMiddleware = (store) => (next) => (action) => {
// RESET_PANELS: GNB 네비게이션 또는 완전 초기화
case types.RESET_PANELS: {
if (DEBUG_MODE) console.log('[PANEL] RESET_PANELS:', {
if (DEBUG_MODE)
console.log('[PANEL] RESET_PANELS:', {
payload: action.payload,
timestamp: new Date().toISOString()
timestamp: new Date().toISOString(),
});
if (DEBUG_MODE) console.log('[PANEL_HISTORY] Before RESET_PANELS:', store.getState().panelHistory);
if (DEBUG_MODE)
console.log('[PANEL_HISTORY] Before RESET_PANELS:', store.getState().panelHistory);
// 모든 RESET_PANELS를 기록
const isGNB = isGNBCall();
@@ -164,9 +229,15 @@ export const panelHistoryMiddleware = (store) => (next) => (action) => {
const firstPanel = action.payload[0];
if (firstPanel && firstPanel.name) {
const isOnTop = calculateIsOnTop(firstPanel.name); // 🎯 isOnTop 계산
if (DEBUG_MODE) console.log('[PANEL_DEBUG] RESET_PANELS to:', firstPanel.name, { isGNB, isOnTop, fromResetPanel: true });
if (DEBUG_MODE)
console.log('[PANEL_DEBUG] RESET_PANELS to:', firstPanel.name, {
isGNB,
isOnTop,
fromResetPanel: true,
});
// RESET 이동을 히스토리에 기록
store.dispatch(enqueuePanelHistory(
store.dispatch(
enqueuePanelHistory(
firstPanel.name,
firstPanel.panelInfo || {},
'RESET',
@@ -174,17 +245,23 @@ export const panelHistoryMiddleware = (store) => (next) => (action) => {
isGNB, // TabLayout/TIconButton이면 true
true, // fromResetPanel: 항상 true
isOnTop // 🎯 isOnTop 정보 추가
));
)
);
}
} else {
// 완전 초기화 (payload가 없는 경우 - HomePanel, 앱 초기화 등)
if (DEBUG_MODE) console.log('[PANEL_DEBUG] Complete panel history reset (payload empty)', { isGNB, fromResetPanel: true });
if (DEBUG_MODE)
console.log('[PANEL_DEBUG] Complete panel history reset (payload empty)', {
isGNB,
fromResetPanel: true,
});
store.dispatch(clearPanelHistory());
// HomePanel 초기화 기록 (앱 시작 시)
if (DEBUG_MODE) console.log('[PANEL_DEBUG] Recording initial HomePanel');
const isOnTop = calculateIsOnTop('homepanel'); // 🎯 isOnTop 계산
store.dispatch(enqueuePanelHistory(
store.dispatch(
enqueuePanelHistory(
'homepanel',
{},
'APP_INITIALIZE',
@@ -192,7 +269,8 @@ export const panelHistoryMiddleware = (store) => (next) => (action) => {
isGNB, // TIconButton Home 버튼이면 true
true, // fromResetPanel: true
isOnTop // 🎯 isOnTop 정보 추가
));
)
);
}
// PanelHistory 상태 로그 (초기화 후)
@@ -200,7 +278,11 @@ export const panelHistoryMiddleware = (store) => (next) => (action) => {
if (DEBUG_MODE) {
const stateAfter = store.getState();
const panelHistoryAfter = stateAfter.panelHistory;
console.log('[PANEL_HISTORY] After RESET_PANELS:', panelHistoryAfter);
const panelsAfter = stateAfter.panels.panels;
console.log('[PANEL_HISTORY] After RESET_PANELS:', {
panelHistory: panelHistoryAfter,
panels: panelsAfter,
});
}
};

View File

@@ -599,6 +599,13 @@ const HomePanel = ({ isOnTop }) => {
if (isInitialRender.current) {
isInitialRender.current = false;
// ✅ 마운트 시 banner0 자동 재생 설정
console.log('[HomePanel] 마운트 - banner0 재생 시도', videoPlay);
videoPlayIntentRef.current = { bannerId: 'banner0' };
console.log('[HomePanel] videoPlayIntentRef 설정:', videoPlayIntentRef.current);
videoPlay.playVideo('banner0', { reason: 'mount-init' });
console.log('[HomePanel] playVideo 호출 완료');
if (isDeepLink || (!panels.length && !panelInfo.focusedContainerId)) {
dispatch(changeAppStatus({ showLoadingPanel: { show: true, type: 'wait' } }));
dispatch(getHomeMainContents());
@@ -684,6 +691,28 @@ const HomePanel = ({ isOnTop }) => {
}
}, [isOnTop]);
// ✅ HomePanel 활성화 조건: 최상단 패널 또는 PlayerPanel 아래의 두 번째 패널
// 비디오가 재생이 아니면 videoPlayIntentRef의 bannerId로 비디오 재생
useEffect(() => {
const isHomePanelActive =
panels[0]?.name === panel_names.HOME_PANEL ||
(panels.length >= 2 &&
panels[0]?.name === panel_names.PLAYER_PANEL &&
panels[1]?.name === panel_names.HOME_PANEL);
if (isHomePanelActive && videoPlayIntentRef.current?.bannerId) {
const bannerId = videoPlayIntentRef.current.bannerId;
const currentPlaying = videoPlay.getCurrentPlayingBanner();
// 이미 재생 중인 배너가 다르면 새로 재생
if (currentPlaying !== bannerId) {
videoPlay.playVideo(bannerId, {
reason: 'homePanel-active',
});
}
}
}, [panels]);
useEffect(() => {
return () => {
const c = Spotlight.getCurrent();

View File

@@ -6,11 +6,10 @@ import { useDispatch, useSelector } from 'react-redux';
import platform from '@enact/core/platform';
import Spotlight from '@enact/spotlight';
import defaultWatchItem from '../../../assets/images/img-alert-banner-st@3x.png';
// 테스트용 - TODO: 메인 홈 화면에 나와야 하는 이미지들 추가 후 preloadImages에 추가
import testImage from '../../../assets/images/img-banner-myinfo-login@3x.png';
import defaultImageItem from '../../../assets/images/img-thumb-empty-product@3x.png';
// import testImage from '../../../assets/images/img-banner-myinfo-login@3x.png';
// import defaultImageItem from '../../../assets/images/img-thumb-empty-product@3x.png';
import LoadingPreloadImage from '../../../assets/images/intro/splash_02_stop.webp';
import LoadingAnimation from '../../../assets/images/intro/splash_03_end.webp';
import LoadingCompleteImage from '../../../assets/images/intro/splash_04_end.webp';
@@ -80,7 +79,7 @@ import WelcomeEventPanel from '../WelcomeEventPanel/WelcomeEventPanel';
import css from './MainView.module.less';
// DEBUG_MODE 상수 - true일 때만 console.log 출력
const DEBUG_MODE = false;
const DEBUG_MODE = true;
const preloadImages = [
LoadingPreloadImage,
@@ -209,11 +208,14 @@ export default function MainView({ className, initService }) {
// 단독 패널 체크 - CheckOutPanel, CartPanel 등 단독으로 렌더링되어야 하는 패널들
if (DEBUG_MODE) {
console.log(`[MainView] 🔍 Top panel name: ${topPanel?.name}`);
console.log(`[MainView] 🔍 isStandalonePanel check:`, isStandalonePanel(topPanel?.name));
console.log(`[MainView] 🔍 STANDALONE_PANELS:`, STANDALONE_PANELS);
console.log(`[PANEL_MainView] 🔍 Top panel name: ${topPanel?.name}`);
console.log(
`[MainView] 🔍 All panels:`,
`[PANEL_MainView] 🔍 isStandalonePanel check:`,
isStandalonePanel(topPanel?.name)
);
console.log(`[PANEL_MainView] 🔍 STANDALONE_PANELS:`, STANDALONE_PANELS);
console.log(
`[PANEL_MainView] 🔍 All panels:`,
panels.map((p) => ({ name: p.name, hasModal: !!p.panelInfo?.modal }))
);
}
@@ -258,6 +260,7 @@ export default function MainView({ className, initService }) {
renderingPanels = panels.slice(-1);
}
}
return (
<>
{(isHomeOnTop ||