[251014] docs(views): [251014] VoicePanel

🕐 커밋 시간: 2025. 10. 14. 14:56:58

📊 변경 통계:
  • 총 파일: 21개
  • 추가: +714줄
  • 삭제: -69줄

📁 추가된 파일:
  + com.twin.app.shoptime/luna.md
  + com.twin.app.shoptime/src/actions/voiceActions.js
  + com.twin.app.shoptime/src/lunaSend/voice.js
  + com.twin.app.shoptime/src/reducers/voiceReducer.js
  + com.twin.app.shoptime/src/views/VoicePanel/mockLogData.js
  + com.twin.app.shoptime/vui.md
  + com.twin.app.shoptime/webos-meta/appinfo.bakcup.json

📝 수정된 파일:
  ~ com.twin.app.shoptime/src/actions/actionTypes.js
  ~ com.twin.app.shoptime/src/actions/mediaActions.js
  ~ com.twin.app.shoptime/src/components/VideoPlayer/Video.js
  ~ com.twin.app.shoptime/src/lunaSend/index.js
  ~ com.twin.app.shoptime/src/store/store.js
  ~ com.twin.app.shoptime/src/views/DetailPanel/ProductAllSection/ProductAllSection.jsx
  ~ com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/ProductVideo/ProductVideo.jsx
  ~ com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/ProductVideo/ProductVideo.module.less
  ~ com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/ProductVideo/ProductVideo.v2.jsx
  ~ com.twin.app.shoptime/src/views/MediaPanel/MediaPanel.jsx
  ~ com.twin.app.shoptime/src/views/MediaPanel/MediaPanel.module.less
  ~ com.twin.app.shoptime/src/views/VoicePanel/VoicePanel.jsx
  ~ com.twin.app.shoptime/src/views/VoicePanel/VoicePanel.module.less
  ~ com.twin.app.shoptime/webos-meta/appinfo.json

🔧 함수 변경 내용:
  📄 com.twin.app.shoptime/src/actions/mediaActions.js (javascript):
     Added: switchMediaToModal()
  📄 com.twin.app.shoptime/src/views/DetailPanel/ProductAllSection/ProductAllSection.jsx (javascript):
    🔄 Modified: extractProductMeta()
  📄 com.twin.app.shoptime/src/views/VoicePanel/VoicePanel.module.less (unknown):
     Added: gradient()
  📄 com.twin.app.shoptime/luna.md (md파일):
     Added: Layer(), Functions(), LS2Request(), PalmServiceBridge(), Bus(), function(), instance(), cancel(), deleteInstance(), dispatch(), createToast(), getSystemSettings(), onSuccess(), getConnectionStatus(), useEffect()
  📄 com.twin.app.shoptime/src/actions/voiceActions.js (javascript):
     Added: addLog(), handleSelectIntent(), handleScrollIntent()
  📄 com.twin.app.shoptime/src/views/VoicePanel/mockLogData.js (javascript):
     Added: getRandomElement(), generateMockLogs()
  📄 com.twin.app.shoptime/vui.md (md파일):
     Added: Interface(), Commands(), Controls(), Format()

🔧 주요 변경 내용:
  • 타입 시스템 안정성 강화
  • 핵심 비즈니스 로직 개선
  • UI 컴포넌트 아키텍처 개선
  • 개발 문서 및 가이드 개선
  • 로깅 시스템 개선
This commit is contained in:
2025-10-14 14:57:02 +09:00
parent 997415f836
commit c823587eaf
21 changed files with 2891 additions and 404 deletions

View File

@@ -113,28 +113,43 @@ const MediaPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props
// modal 스타일 설정
useEffect(() => {
if (panelInfo.modal && panelInfo.modalContainerId) {
// modal 모드: modalContainerId 기반으로 위치와 크기 계산
const node = document.querySelector(`[data-spotlight-id="${panelInfo.modalContainerId}"]`);
if (node) {
const { width, height, top, left } = node.getBoundingClientRect();
// ProductVideo의 padding(6px * 2)과 추가 여유를 고려하여 크기 조정
// 비디오가 오른쪽으로 넘치지 않도록 충분한 여유 확보
const paddingOffset = 6 * 2; // padding 양쪽
const extraMargin = 6 * 2; // 추가 여유 (포커스 테두리 + 비디오 비율 고려)
const totalOffset = paddingOffset + extraMargin; // 24px
const adjustedWidth = width - totalOffset;
const adjustedHeight = height - totalOffset;
const adjustedTop = top + totalOffset / 2;
const adjustedLeft = left + totalOffset / 2;
const style = {
width: width + 'px',
height: height + 'px',
top: top + 'px',
left: left + 'px',
width: adjustedWidth + 'px',
height: adjustedHeight + 'px',
maxWidth: adjustedWidth + 'px',
maxHeight: adjustedHeight + 'px',
top: adjustedTop + 'px',
left: adjustedLeft + 'px',
position: 'fixed',
overflow: 'visible',
overflow: 'hidden', // visible → hidden으로 변경하여 넘치는 부분 숨김
};
setModalStyle(style);
let scale = 1;
if (typeof window === 'object') {
scale = width / window.innerWidth;
scale = adjustedWidth / window.innerWidth;
setModalScale(scale);
}
} else {
setModalStyle(panelInfo.modalStyle || {});
setModalScale(panelInfo.modalScale || 1);
}
} else if (isOnTop && !panelInfo.modal && videoPlayer.current) {
} else if (isOnTop && !panelInfo.modal && !panelInfo.isMinimized && videoPlayer.current) {
if (videoPlayer.current?.getMediaState()?.paused) {
videoPlayer.current.play();
}
@@ -263,8 +278,9 @@ const MediaPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props
const onEnded = useCallback(
(e) => {
// console.log('[MediaPanel] Video ended');
// 비디오 종료 시 패널 닫기
console.log('[MediaPanel] Video ended');
// continuousPlay는 MediaPlayer(VideoPlayer) 컴포넌트 내부에서 loop 속성으로 처리
// onEnded가 호출되면 loop=false 인 경우이므로 패널을 닫음
Spotlight.pause();
setTimeout(() => {
Spotlight.resume();
@@ -290,23 +306,48 @@ const MediaPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props
// console.log('[MediaPanel] ========== Rendering ==========');
// console.log('[MediaPanel] isOnTop:', isOnTop);
// console.log('[MediaPanel] panelInfo:', JSON.stringify(panelInfo, null, 2));
// console.log('[MediaPanel] panelInfo.modal:', panelInfo.modal);
// console.log('[MediaPanel] panelInfo.isMinimized:', panelInfo.isMinimized);
// console.log('[MediaPanel] panelInfo.isPaused:', panelInfo.isPaused);
// console.log('[MediaPanel] currentPlayingUrl:', currentPlayingUrl);
// console.log('[MediaPanel] hasVideoPlayer:', !!videoPlayer.current);
// classNames 적용 상태 확인
// console.log('[MediaPanel] ========== ClassNames Analysis ==========');
// console.log('[MediaPanel] css.videoContainer:', css.videoContainer);
// console.log('[MediaPanel] Condition [panelInfo.modal && !panelInfo.isMinimized]:', panelInfo.modal && !panelInfo.isMinimized);
// console.log('[MediaPanel] css.modal:', css.modal);
// console.log('[MediaPanel] Condition [panelInfo.isMinimized]:', panelInfo.isMinimized);
// console.log('[MediaPanel] css["modal-minimized"]:', css['modal-minimized']);
// console.log('[MediaPanel] Condition [!isOnTop]:', !isOnTop);
// console.log('[MediaPanel] css.background:', css.background);
const appliedClassNames = classNames(
css.videoContainer,
panelInfo.modal && !panelInfo.isMinimized && css.modal,
panelInfo.isMinimized && css['modal-minimized'],
!isOnTop && css.background
);
// console.log('[MediaPanel] Final Applied ClassNames:', appliedClassNames);
// console.log('[MediaPanel] modalStyle:', modalStyle);
// console.log('[MediaPanel] modalScale:', modalScale);
// console.log('[MediaPanel] ===============================================');
// minimized 상태일 때는 spotlightRestrict 해제 (포커스 이동 허용)
const containerSpotlightRestrict = panelInfo.isMinimized ? 'none' : 'self-only';
return (
<TPanel
isTabActivated={false}
{...props}
className={classNames(
css.videoContainer,
panelInfo.modal && css.modal,
!isOnTop && css.background
)}
className={appliedClassNames}
handleCancel={onClickBack}
spotlightId={spotlightId}
>
<Container spotlightRestrict="self-only" spotlightId="spotlightId-media-video-container">
<Container
spotlightRestrict={containerSpotlightRestrict}
spotlightId="spotlightId-media-video-container"
>
{currentPlayingUrl && (
<VideoPlayer
setApiProvider={getPlayer}
@@ -319,6 +360,7 @@ const MediaPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props
spotlightDisabled={panelInfo.modal}
isYoutube={isYoutube}
src={currentPlayingUrl}
loop={panelInfo.continuousPlay || false}
style={panelInfo.modal ? modalStyle : {}}
modalScale={panelInfo.modal ? modalScale : 1}
modalClassName={panelInfo.modal && panelInfo.modalClassName}