[251217] fix: VOD 경과시간 표시

🕐 커밋 시간: 2025. 12. 17. 16:09:01

📊 변경 통계:
  • 총 파일: 2개
  • 추가: +18줄
  • 삭제: -205줄

📝 수정된 파일:
  ~ com.twin.app.shoptime/src/components/VideoPlayer/MediaPlayer.jsx
  ~ com.twin.app.shoptime/src/components/VideoPlayer/VideoPlayer.js

🔧 주요 변경 내용:
  • UI 컴포넌트 아키텍처 개선
  • 코드 정리 및 최적화

Performance: 코드 최적화로 성능 개선 기대
This commit is contained in:
2025-12-17 16:09:01 +09:00
parent a7161b8a80
commit ec76d2cfc9
2 changed files with 18 additions and 205 deletions

View File

@@ -840,9 +840,11 @@ const VideoPlayerBase = class extends React.Component {
this.state.mediaSliderVisible === nextState.mediaSliderVisible &&
this.state.loading === nextState.loading &&
this.props.loading === nextProps.loading &&
(this.state.currentTime !== nextState.currentTime ||
this.state.proportionPlayed !== nextState.proportionPlayed ||
this.state.sliderTooltipTime !== nextState.sliderTooltipTime)
this.state.currentTime === nextState.currentTime &&
this.state.proportionPlayed === nextState.proportionPlayed &&
this.state.sliderTooltipTime === nextState.sliderTooltipTime &&
this.state.mediaControlsVisible === nextState.mediaControlsVisible &&
this.state.bottomControlsRendered === nextState.bottomControlsRendered
) {
return false;
}
@@ -1279,14 +1281,11 @@ const VideoPlayerBase = class extends React.Component {
sourceUnavailable: true,
proportionPlayed: 0,
proportionLoaded: 0,
bottomControlsRendered: true,
});
if (!this.props.noAutoShowMediaControls) {
if (!this.state.bottomControlsRendered) {
this.renderBottomControl.idle();
} else {
this.showControls();
}
this.showControls();
}
};

View File

@@ -855,9 +855,11 @@ const VideoPlayerBase = class extends React.Component {
this.state.mediaSliderVisible === nextState.mediaSliderVisible &&
this.state.loading === nextState.loading &&
this.props.loading === nextProps.loading &&
(this.state.currentTime !== nextState.currentTime ||
this.state.proportionPlayed !== nextState.proportionPlayed ||
this.state.sliderTooltipTime !== nextState.sliderTooltipTime)
this.state.currentTime === nextState.currentTime &&
this.state.proportionPlayed === nextState.proportionPlayed &&
this.state.sliderTooltipTime === nextState.sliderTooltipTime &&
this.state.mediaControlsVisible === nextState.mediaControlsVisible &&
this.state.bottomControlsRendered === nextState.bottomControlsRendered
) {
return false;
}
@@ -1423,14 +1425,11 @@ const VideoPlayerBase = class extends React.Component {
sourceUnavailable: true,
proportionPlayed: 0,
proportionLoaded: 0,
bottomControlsRendered: true,
});
if (!this.props.noAutoShowMediaControls) {
if (!this.state.bottomControlsRendered) {
this.renderBottomControl.idle();
} else {
this.showControls();
}
this.showControls();
}
};
@@ -1636,103 +1635,6 @@ const VideoPlayerBase = class extends React.Component {
updatedState.thumbnailUrl = null;
}
this.setState(updatedState);
// Redux에 비디오 재생 상태 업데이트 (기존 로직 유지)
if (this.props.dispatch) {
// 🔥 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) {
dlog('🔄 [PlayerPanel][VideoPlayer] Event-driven Redux update', {
eventType: ev.type,
videoState: updatedState,
updateState,
timestamp: new Date().toISOString(),
});
}
// 🔍 Redux dispatch 확인
dlog('📤 [PlayerPanel][VideoPlayer] Dispatching Redux update', {
eventType: ev.type,
updateState,
hasDispatch: !!this.props.dispatch,
propsVideoPlayState: this.props.videoPlayState,
});
this.props.dispatch(updateVideoPlayState(updateState));
}
} else {
derror('❌ [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') {
dlog('🔍 [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) {
dlog('🔄 [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,
});
}
}
};
renderBottomControl = new Job(() => {
@@ -1744,7 +1646,6 @@ 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
@@ -1752,19 +1653,13 @@ const VideoPlayerBase = class extends React.Component {
* @public
*/
getMediaState = () => {
// Redux 상태를 우선적으로 사용하여 일관성 보장
// Redux 상태가 없으면 내부 상태 사용 (fallback)
const reduxState = this.props.videoPlayState;
return {
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,
currentTime: this.state.currentTime,
duration: this.state.duration,
paused: this.state.paused,
playbackRate: this.video?.playbackRate,
proportionLoaded: this.state.proportionLoaded,
proportionPlayed: this.state.proportionPlayed,
// Redux 상태 정보도 포함
isPlaying: reduxState?.isPlaying ?? !this.state.paused,
};
};
@@ -1793,16 +1688,7 @@ const VideoPlayerBase = class extends React.Component {
* @public
*/
play = () => {
dlog('🟢 [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) {
dwarn('⚠️ [PlayerPanel][VideoPlayer] play() aborted - source unavailable');
return;
}
@@ -1814,19 +1700,6 @@ 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,
})
);
}
};
/**
@@ -1837,16 +1710,7 @@ const VideoPlayerBase = class extends React.Component {
* @public
*/
pause = () => {
dlog('🔴 [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) {
dwarn('⚠️ [VideoPlayer] pause() aborted - source unavailable');
return;
}
@@ -1858,22 +1722,6 @@ 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,
};
dlog('📤 [VideoPlayer] Dispatching pause state', pauseState);
this.props.dispatch(updateVideoPlayState(pauseState));
} else {
dwarn('⚠️ [VideoPlayer] No dispatch prop available - Redux state not updated');
}
};
/**
@@ -1885,15 +1733,6 @@ const VideoPlayerBase = class extends React.Component {
* @public
*/
seek = (timeIndex) => {
dlog('⏩ [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 &&
@@ -1904,34 +1743,9 @@ const VideoPlayerBase = class extends React.Component {
const actualSeekTime =
timeIndex >= this.video.duration ? this.video.duration - 1 : timeIndex;
this.video.currentTime = actualSeekTime;
dlog('⏩ [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,
};
dlog('📤 [VideoPlayer] Dispatching seek state', seekState);
this.props.dispatch(updateVideoPlayState(seekState));
} else {
dwarn('⚠️ [VideoPlayer] No dispatch prop available - Redux state not updated');
}
} else {
derror('❌ [VideoPlayer] seek failed - disabled or source unavailable');
forward('onSeekFailed', {}, this.props);
}
} else {
derror('❌ [VideoPlayer] seek failed - no video element');
}
};