import React, { useState, useEffect, useRef } from 'react';
import { useTranslation } from 'react-i18next';
import { getSocialAccounts, uploadToSocial, waitForUploadComplete, TokenExpiredError, handleSocialReconnect, getAutoSeoYoutube } from '../utils/api';
import { SocialAccount, VideoListItem, SocialUploadStatusResponse } from '../types/api';
import UploadProgressModal, { UploadStatus } from './UploadProgressModal';
import { useTutorial } from './Tutorial/useTutorial';
import { TUTORIAL_KEYS } from './Tutorial/tutorialSteps';
import TutorialOverlay from './Tutorial/TutorialOverlay';
interface SocialPostingModalProps {
isOpen: boolean;
onClose: () => void;
video: VideoListItem | null;
onGoToCalendar?: () => void;
}
type PrivacyType = 'public' | 'unlisted' | 'private';
type PublishTimeType = 'now' | 'schedule';
// 플랫폼별 아이콘 경로
const getPlatformIcon = (platform: string) => {
switch (platform) {
case 'youtube':
return '/assets/images/social-youtube.png';
case 'instagram':
return '/assets/images/social-instagram.png';
default:
return '/assets/images/social-youtube.png';
}
};
// 미니 캘린더 컴포넌트
const MiniCalendar: React.FC<{
selectedDate: Date | null;
onSelect: (date: Date) => void;
minDate?: Date;
}> = ({ selectedDate, onSelect, minDate }) => {
const today = new Date();
const [viewYear, setViewYear] = useState(selectedDate?.getFullYear() ?? today.getFullYear());
const [viewMonth, setViewMonth] = useState(selectedDate?.getMonth() ?? today.getMonth());
const DAY_LABELS = ['일', '월', '화', '수', '목', '금', '토'];
const firstDay = new Date(viewYear, viewMonth, 1).getDay();
const daysInMonth = new Date(viewYear, viewMonth + 1, 0).getDate();
const daysInPrevMonth = new Date(viewYear, viewMonth, 0).getDate();
const cells: { date: Date; currentMonth: boolean }[] = [];
for (let i = firstDay - 1; i >= 0; i--) {
cells.push({ date: new Date(viewYear, viewMonth - 1, daysInPrevMonth - i), currentMonth: false });
}
for (let d = 1; d <= daysInMonth; d++) {
cells.push({ date: new Date(viewYear, viewMonth, d), currentMonth: true });
}
while (cells.length % 7 !== 0) {
cells.push({ date: new Date(viewYear, viewMonth + 1, cells.length - daysInMonth - firstDay + 1), currentMonth: false });
}
const isSameDay = (a: Date, b: Date) =>
a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
const isDisabled = (date: Date) => {
if (!minDate) return false;
const min = new Date(minDate); min.setHours(0,0,0,0);
const d = new Date(date); d.setHours(0,0,0,0);
return d < min;
};
const prevMonth = () => {
if (viewMonth === 0) { setViewYear(y => y - 1); setViewMonth(11); }
else setViewMonth(m => m - 1);
};
const nextMonth = () => {
if (viewMonth === 11) { setViewYear(y => y + 1); setViewMonth(0); }
else setViewMonth(m => m + 1);
};
return (
{viewYear}년 {viewMonth + 1}월
{DAY_LABELS.map(d => (
{d}
))}
{cells.map((cell, i) => {
const disabled = isDisabled(cell.date);
const isSelected = selectedDate ? isSameDay(cell.date, selectedDate) : false;
const isToday = isSameDay(cell.date, today);
return (
);
})}
);
};
const SocialPostingModal: React.FC = ({
isOpen,
onClose,
video,
onGoToCalendar,
}) => {
const { t } = useTranslation();
const tutorial = useTutorial();
const [socialAccounts, setSocialAccounts] = useState([]);
const [selectedChannel, setSelectedChannel] = useState('');
const [title, setTitle] = useState('');
const [description, setDescription] = useState('');
const [tags, setTags] = useState('');
const [privacy, setPrivacy] = useState('public');
const [publishTime, setPublishTime] = useState('now');
const [scheduledDate, setScheduledDate] = useState(null);
const [scheduledHour, setScheduledHour] = useState(12);
const [scheduledMinute, setScheduledMinute] = useState(0);
const [isLoadingAccounts, setIsLoadingAccounts] = useState(false);
const [isPosting, setIsPosting] = useState(false);
const [isChannelDropdownOpen, setIsChannelDropdownOpen] = useState(false);
const [isPrivacyDropdownOpen, setIsPrivacyDropdownOpen] = useState(false);
const [isLoadingAutoDescription, setIsLoadingAutoDescription] = useState(false);
const [isHorizontalVideo, setIsHorizontalVideo] = useState(false);
const [videoMeta, setVideoMeta] = useState<{ width: number; height: number; duration: number } | null>(null);
const channelDropdownRef = useRef(null);
const privacyDropdownRef = useRef(null);
const hasBeenOpenedRef = useRef(false);
const loadedForTaskIdRef = useRef(null);
const loadedAtRef = useRef(0);
const seoCache = useRef