import { ChangeEvent, useCallback, useRef, useState } from "react"; import { useTranslation } from "react-i18next"; import { useNavigate } from "react-router-dom"; import { Button } from "@/components/buttons/Button"; import { Icon, Icons } from "@/components/Icon"; import { SettingsCard } from "@/components/layout/SettingsCard"; import { Stepper } from "@/components/layout/Stepper"; import { CenterContainer } from "@/components/layout/ThinContainer"; import { Divider } from "@/components/utils/Divider"; import { Heading2, Paragraph } from "@/components/utils/Text"; import { useAuth } from "@/hooks/auth/useAuth"; import { MinimalPageLayout } from "@/pages/layouts/MinimalPageLayout"; import { PageTitle } from "@/pages/parts/util/PageTitle"; import { useAuthStore } from "@/stores/auth"; import { BookmarkMediaItem, useBookmarkStore } from "@/stores/bookmarks"; import { ProgressMediaItem, useProgressStore } from "@/stores/progress"; interface UploadedData { account?: { profile?: { icon: string; colorA: string; colorB: string; }; deviceName?: string; }; bookmarks?: Record; progress?: Record; exportDate?: string; } export function MigrationUploadPage() { const { t } = useTranslation(); const navigate = useNavigate(); const user = useAuthStore(); const { importData } = useAuth(); const fileInputRef = useRef(null); const replaceBookmarks = useBookmarkStore((s) => s.replaceBookmarks); const replaceProgress = useProgressStore((s) => s.replaceItems); const [selectedFile, setSelectedFile] = useState(null); const [status, setStatus] = useState< "idle" | "success" | "error" | "processing" >("idle"); const [uploadedData, setUploadedData] = useState(null); const handleFileButtonClick = () => { if (fileInputRef.current) { fileInputRef.current.click(); } }; const readFile = async (file: File) => { try { setStatus("processing"); const fileContent = await file.text(); const parsedData = JSON.parse(fileContent); // Validate and ensure types match what we expect const validatedData: UploadedData = { ...parsedData, bookmarks: parsedData.bookmarks ? Object.entries(parsedData.bookmarks).reduce( (acc, [id, item]: [string, any]) => { // Ensure type is either "show" or "movie" if ( typeof item.type === "string" && (item.type === "show" || item.type === "movie") ) { acc[id] = { title: item.title || "", year: typeof item.year === "number" ? item.year : undefined, poster: item.poster, type: item.type as "show" | "movie", updatedAt: typeof item.updatedAt === "number" ? item.updatedAt : Date.now(), }; } return acc; }, {} as Record, ) : undefined, progress: parsedData.progress ? Object.entries(parsedData.progress).reduce( (acc, [id, item]: [string, any]) => { // Ensure type is either "show" or "movie" if ( typeof item.type === "string" && (item.type === "show" || item.type === "movie") ) { acc[id] = { title: item.title || "", poster: item.poster, type: item.type as "show" | "movie", updatedAt: typeof item.updatedAt === "number" ? item.updatedAt : Date.now(), year: typeof item.year === "number" ? item.year : undefined, progress: item.progress, episodes: item.episodes || {}, seasons: item.seasons || {}, }; } return acc; }, {} as Record, ) : undefined, }; setUploadedData(validatedData); setStatus("idle"); } catch (error) { console.error("Error parsing JSON file:", error); setStatus("error"); } }; const handleFileChange = (e: ChangeEvent) => { if (e.target.files && e.target.files.length > 0) { setSelectedFile(e.target.files[0]); setStatus("idle"); setUploadedData(null); // Auto-read the file when selected const file = e.target.files[0]; readFile(file); } }; const handleImport = useCallback(() => { if (status === "processing") { return; } if (!uploadedData || !user.account) return; setStatus("processing"); if (uploadedData.bookmarks) { replaceBookmarks(uploadedData.bookmarks); } if (uploadedData.progress) { replaceProgress(uploadedData.progress); } importData( user.account, uploadedData.progress || {}, uploadedData.bookmarks || {}, ) .then(() => { setStatus("success"); }) .catch((error) => { console.error("Error importing data:", error); setStatus("error"); }); }, [ replaceBookmarks, replaceProgress, uploadedData, user.account, importData, status, ]); return (
{t("migration.upload.title")} {t("migration.upload.description")}

{t("migration.upload.file.description")}:

{selectedFile && (
{selectedFile.name} {uploadedData?.exportDate && (
{t("migration.upload.exportedOn")}:{" "} {new Date( uploadedData?.exportDate || "", ).toLocaleDateString()}
)}
)} {status === "processing" && (
{t("migration.upload.status.processing")}
)} {status === "error" && (
{t("migration.upload.status.error")}
)}
{uploadedData && ( {t("migration.upload.dataPreview")}
{t("migration.upload.items.bookmarks")}
{uploadedData.bookmarks ? Object.keys(uploadedData.bookmarks).length : 0}
{t("migration.upload.items.progress")}
{uploadedData.progress ? Object.keys(uploadedData.progress).length : 0}
{status === "success" ? (
{t("migration.upload.status.success")}
) : ( )}
)}
{status === "success" && ( )}
); }