영상생성 완료 화면 및 소셜 미디어 포스팅 화면 UI수정
parent
29e0c7d79c
commit
be32a04dd0
60
index.css
60
index.css
|
|
@ -2053,7 +2053,7 @@
|
|||
.comp2-container {
|
||||
width: calc(100% - 64px);
|
||||
max-width: 1440px;
|
||||
margin: 0 auto;
|
||||
margin: 16px;
|
||||
background: #01393B;
|
||||
border-radius: 24px;
|
||||
padding: 24px;
|
||||
|
|
@ -2099,7 +2099,7 @@
|
|||
.comp2-video-section {
|
||||
flex: 0 0 60%;
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
flex-direction: column;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
|
@ -2110,6 +2110,8 @@
|
|||
position: relative;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.comp2-video-player {
|
||||
|
|
@ -2520,31 +2522,34 @@
|
|||
|
||||
/* 반응형 */
|
||||
@media (max-width: 1024px) {
|
||||
.comp2-page {
|
||||
height: auto;
|
||||
min-height: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.comp2-container {
|
||||
width: calc(100% - 32px);
|
||||
margin: 0 16px;
|
||||
margin: 16px;
|
||||
padding: 16px;
|
||||
flex: none;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.comp2-grid {
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
overflow: visible;
|
||||
flex: none;
|
||||
height: auto;
|
||||
min-height: auto;
|
||||
}
|
||||
|
||||
.comp2-video-section {
|
||||
align-items: center;
|
||||
flex: none;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.comp2-video-wrapper {
|
||||
max-height: 300px;
|
||||
flex: none;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.comp2-video-player {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.comp2-info-section {
|
||||
|
|
@ -2555,8 +2560,16 @@
|
|||
padding-top: 16px;
|
||||
}
|
||||
|
||||
.comp2-info-content {
|
||||
flex: none;
|
||||
height: auto;
|
||||
overflow: visible;
|
||||
min-height: auto;
|
||||
}
|
||||
|
||||
.comp2-buttons {
|
||||
flex-direction: row;
|
||||
margin-top: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -9442,33 +9455,40 @@
|
|||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 1.5rem;
|
||||
padding: 1rem;
|
||||
background-color: #001a1c;
|
||||
border-right: 1px solid rgba(255, 255, 255, 0.08);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.social-posting-preview {
|
||||
width: 100%;
|
||||
max-height: 40vh;
|
||||
height: 80vh;
|
||||
border-right: none;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
}
|
||||
|
||||
.social-posting-video-container {
|
||||
width: 100%;
|
||||
max-width: 280px;
|
||||
aspect-ratio: 9/16;
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
background-color: #000;
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.social-posting-video {
|
||||
.social-posting-video.horizontal {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.social-posting-video.vertical {
|
||||
display: block;
|
||||
max-height: 100%;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
/* Form Section */
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ const SocialPostingModal: React.FC<SocialPostingModalProps> = ({
|
|||
const [isChannelDropdownOpen, setIsChannelDropdownOpen] = useState(false);
|
||||
const [isPrivacyDropdownOpen, setIsPrivacyDropdownOpen] = useState(false);
|
||||
const [isLoadingAutoDescription, setIsLoadingAutoDescription] = useState(false);
|
||||
const [isHorizontalVideo, setIsHorizontalVideo] = useState(false);
|
||||
const channelDropdownRef = useRef<HTMLDivElement>(null);
|
||||
const privacyDropdownRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
|
|
@ -72,6 +73,16 @@ const SocialPostingModal: React.FC<SocialPostingModalProps> = ({
|
|||
return () => document.removeEventListener('mousedown', handleClickOutside);
|
||||
}, []);
|
||||
|
||||
// 모달 열릴 때 배경 스크롤 차단
|
||||
useEffect(() => {
|
||||
if (isOpen) {
|
||||
document.body.style.overflow = 'hidden';
|
||||
} else {
|
||||
document.body.style.overflow = '';
|
||||
}
|
||||
return () => { document.body.style.overflow = ''; };
|
||||
}, [isOpen]);
|
||||
|
||||
// 소셜 계정 로드
|
||||
useEffect(() => {
|
||||
if (isOpen) {
|
||||
|
|
@ -297,9 +308,13 @@ const SocialPostingModal: React.FC<SocialPostingModalProps> = ({
|
|||
<div className="social-posting-video-container">
|
||||
<video
|
||||
src={video.result_movie_url}
|
||||
className="social-posting-video"
|
||||
className={`social-posting-video ${isHorizontalVideo ? 'horizontal' : 'vertical'}`}
|
||||
controls
|
||||
playsInline
|
||||
onLoadedMetadata={(e) => {
|
||||
const v = e.currentTarget;
|
||||
setIsHorizontalVideo(v.videoWidth > v.videoHeight);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -37,12 +37,7 @@ const CompletionContent: React.FC<CompletionContentProps> = ({
|
|||
const [errorMessage, setErrorMessage] = useState<string | null>(null);
|
||||
const [statusMessage, setStatusMessage] = useState('');
|
||||
const [renderProgress, setRenderProgress] = useState(0);
|
||||
const [isPlaying, setIsPlaying] = useState(false);
|
||||
const [progress, setProgress] = useState(0);
|
||||
const [showControls, setShowControls] = useState(true);
|
||||
const videoRef = useRef<HTMLVideoElement>(null);
|
||||
const hasStartedGeneration = useRef(false);
|
||||
const hideControlsTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
|
||||
// 소셜 미디어 포스팅 모달
|
||||
const [showSocialModal, setShowSocialModal] = useState(false);
|
||||
|
|
@ -284,62 +279,6 @@ const CompletionContent: React.FC<CompletionContentProps> = ({
|
|||
}
|
||||
}, []);
|
||||
|
||||
const togglePlayPause = () => {
|
||||
if (!videoRef.current || !videoUrl) return;
|
||||
if (isPlaying) {
|
||||
videoRef.current.pause();
|
||||
setShowControls(true);
|
||||
if (hideControlsTimer.current) clearTimeout(hideControlsTimer.current);
|
||||
} else {
|
||||
videoRef.current.play();
|
||||
hideControlsTimer.current = setTimeout(() => {
|
||||
setShowControls(false);
|
||||
}, 3000);
|
||||
}
|
||||
setIsPlaying(!isPlaying);
|
||||
};
|
||||
|
||||
const handleTimeUpdate = () => {
|
||||
if (videoRef.current && videoRef.current.duration > 0) {
|
||||
setProgress((videoRef.current.currentTime / videoRef.current.duration) * 100);
|
||||
}
|
||||
};
|
||||
|
||||
const handleVideoEnded = () => {
|
||||
setIsPlaying(false);
|
||||
setProgress(0);
|
||||
setShowControls(true);
|
||||
};
|
||||
|
||||
const resetHideControlsTimer = () => {
|
||||
if (hideControlsTimer.current) {
|
||||
clearTimeout(hideControlsTimer.current);
|
||||
}
|
||||
setShowControls(true);
|
||||
if (isPlaying) {
|
||||
hideControlsTimer.current = setTimeout(() => {
|
||||
setShowControls(false);
|
||||
}, 3000);
|
||||
}
|
||||
};
|
||||
|
||||
const handleVideoMouseEnter = () => {
|
||||
resetHideControlsTimer();
|
||||
};
|
||||
|
||||
const handleVideoMouseMove = () => {
|
||||
resetHideControlsTimer();
|
||||
};
|
||||
|
||||
const handleVideoMouseLeave = () => {
|
||||
if (isPlaying) {
|
||||
if (hideControlsTimer.current) clearTimeout(hideControlsTimer.current);
|
||||
hideControlsTimer.current = setTimeout(() => {
|
||||
setShowControls(false);
|
||||
}, 1000);
|
||||
}
|
||||
};
|
||||
|
||||
const handleDownload = () => {
|
||||
if (videoUrl) {
|
||||
const link = document.createElement('a');
|
||||
|
|
@ -374,7 +313,7 @@ const CompletionContent: React.FC<CompletionContentProps> = ({
|
|||
// 비디오 해상도 계산
|
||||
const getVideoResolution = () => {
|
||||
const savedRatio = localStorage.getItem('castad_video_ratio');
|
||||
return savedRatio === 'horizontal' ? '1234×720' : '720×1234';
|
||||
return savedRatio === 'horizontal' ? '1280×720' : '720×1280';
|
||||
};
|
||||
|
||||
// 파일명 생성
|
||||
|
|
@ -403,7 +342,7 @@ const CompletionContent: React.FC<CompletionContentProps> = ({
|
|||
<div className="comp2-grid">
|
||||
{/* 왼쪽: 영상 */}
|
||||
<div className="comp2-video-section">
|
||||
<div className="comp2-video-wrapper" onMouseEnter={handleVideoMouseEnter} onMouseMove={handleVideoMouseMove} onMouseLeave={handleVideoMouseLeave}>
|
||||
<div className="comp2-video-wrapper">
|
||||
{isLoading ? (
|
||||
<div className="comp2-video-loading">
|
||||
<div className="loading-spinner">
|
||||
|
|
@ -426,47 +365,12 @@ const CompletionContent: React.FC<CompletionContentProps> = ({
|
|||
</button>
|
||||
</div>
|
||||
) : videoUrl ? (
|
||||
<>
|
||||
<video
|
||||
ref={videoRef}
|
||||
src={videoUrl}
|
||||
className="comp2-video-player"
|
||||
onTimeUpdate={handleTimeUpdate}
|
||||
onEnded={handleVideoEnded}
|
||||
onClick={togglePlayPause}
|
||||
/>
|
||||
<div className={`comp2-video-controls ${showControls ? 'visible' : 'hidden'}`}>
|
||||
<div className="comp2-progress-bar">
|
||||
<div className="comp2-progress-fill" style={{ width: `${progress}%` }}></div>
|
||||
</div>
|
||||
<div className="comp2-controls-row">
|
||||
<button className="comp2-play-btn" onClick={togglePlayPause}>
|
||||
{isPlaying ? (
|
||||
<svg width="32" height="32" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z"/>
|
||||
</svg>
|
||||
) : (
|
||||
<svg width="32" height="32" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M8 5v14l11-7z"/>
|
||||
</svg>
|
||||
)}
|
||||
</button>
|
||||
<div className="comp2-volume-control">
|
||||
<svg width="32" height="32" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02z"/>
|
||||
</svg>
|
||||
<div className="comp2-volume-bar">
|
||||
<div className="comp2-volume-fill"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="comp2-time-display">
|
||||
<span className="comp2-time-current">00:04</span>
|
||||
<span className="comp2-time-divider">/</span>
|
||||
<span className="comp2-time-total">00:34</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
<video
|
||||
src={videoUrl}
|
||||
className="comp2-video-player"
|
||||
controls
|
||||
playsInline
|
||||
/>
|
||||
) : (
|
||||
<div className="comp2-video-placeholder"></div>
|
||||
)}
|
||||
|
|
|
|||
Loading…
Reference in New Issue