"use client"; import { invoke } from "@tauri-apps/api/core"; import { open } from "@tauri-apps/plugin-dialog"; import { useCallback, useEffect, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; import { FaFolder } from "react-icons/fa"; import { toast } from "sonner"; import { LoadingButton } from "@/components/loading-button"; import { Alert, AlertDescription } from "@/components/ui/alert"; import { AnimatedTabs, AnimatedTabsContent, AnimatedTabsList, AnimatedTabsTrigger, } from "@/components/ui/animated-tabs"; import { Button } from "@/components/ui/button"; import { Dialog, DialogContent, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { WayfernConfigForm } from "@/components/wayfern-config-form"; import { useBrowserSupport } from "@/hooks/use-browser-support"; import { useProxyEvents } from "@/hooks/use-proxy-events"; import { parseBackendError, translateBackendError } from "@/lib/backend-errors"; import { getBrowserDisplayName, getBrowserIcon } from "@/lib/browser-utils"; import { cn } from "@/lib/utils"; import type { DetectedProfile, WayfernConfig } from "@/types"; import { RippleButton } from "./ui/ripple"; const getMappedBrowser = (browser: string): "camoufox" | "wayfern" => { if (["firefox", "firefox-developer", "zen"].includes(browser)) return "camoufox"; return "wayfern"; }; interface ImportProfileDialogProps { isOpen: boolean; onClose: () => void; crossOsUnlocked?: boolean; subPage?: boolean; } export function ImportProfileDialog({ isOpen, onClose, crossOsUnlocked, subPage, }: ImportProfileDialogProps) { const { t } = useTranslation(); const [detectedProfiles, setDetectedProfiles] = useState( [], ); const [isLoading, setIsLoading] = useState(false); const [isImporting, setIsImporting] = useState(false); const [importMode, setImportMode] = useState<"auto-detect" | "manual">( "auto-detect", ); const [currentStep, setCurrentStep] = useState<"select" | "configure">( "select", ); const [wayfernConfig, setWayfernConfig] = useState({}); const [selectedProxyId, setSelectedProxyId] = useState(); // Auto-detect state const [selectedDetectedProfile, setSelectedDetectedProfile] = useState< string | null >(null); const [autoDetectProfileName, setAutoDetectProfileName] = useState(""); // Manual import state const [manualBrowserType, setManualBrowserType] = useState( null, ); const [manualProfilePath, setManualProfilePath] = useState(""); const [manualProfileName, setManualProfileName] = useState(""); const { supportedBrowsers, isLoading: isLoadingSupport } = useBrowserSupport(); const { storedProxies } = useProxyEvents(); // Firefox-based browsers map to the deprecated Camoufox and can no longer be // imported (the backend rejects them); only offer Chromium-family sources. const importableBrowsers = supportedBrowsers.filter( (browser) => getMappedBrowser(browser) === "wayfern", ); const loadDetectedProfiles = useCallback(async () => { setIsLoading(true); try { const profiles = await invoke( "detect_existing_profiles", ); setDetectedProfiles(profiles); if (profiles.length === 0) { setImportMode("manual"); } else { setSelectedDetectedProfile(profiles[0].path); const profile = profiles[0]; const browserName = getBrowserDisplayName(profile.browser); const defaultName = `Imported ${browserName} Profile`; setAutoDetectProfileName(defaultName); } } catch (error) { console.error("Failed to detect existing profiles:", error); toast.error(t("importProfile.detectFailed")); } finally { setIsLoading(false); } }, [t]); const selectedProfile = detectedProfiles.find( (p) => p.path === selectedDetectedProfile, ); const handleBrowseFolder = async () => { try { const selected = await open({ directory: true, multiple: false, title: t("importProfile.selectFolderTitle"), }); if (selected && typeof selected === "string") { setManualProfilePath(selected); } } catch (error) { console.error("Failed to open folder dialog:", error); toast.error(t("importProfile.folderDialogFailed")); } }; const handleImport = useCallback(async () => { let sourcePath: string; let browserType: string; let newProfileName: string; if (importMode === "auto-detect") { if (!selectedDetectedProfile || !autoDetectProfileName.trim()) { toast.error(t("importProfile.selectAndName")); return; } const profile = detectedProfiles.find( (p) => p.path === selectedDetectedProfile, ); if (!profile) { toast.error(t("importProfile.profileNotFound")); return; } sourcePath = profile.path; browserType = profile.browser; newProfileName = autoDetectProfileName.trim(); } else { if ( !manualBrowserType || !manualProfilePath.trim() || !manualProfileName.trim() ) { toast.error(t("importProfile.fillFields")); return; } sourcePath = manualProfilePath.trim(); browserType = manualBrowserType; newProfileName = manualProfileName.trim(); } const mappedBrowser = importMode === "auto-detect" && selectedProfile ? getMappedBrowser(selectedProfile.mapped_browser) : getMappedBrowser(browserType); setIsImporting(true); try { await invoke("import_browser_profile", { sourcePath, browserType, newProfileName, proxyId: selectedProxyId ?? null, // Camoufox import is deprecated/blocked; only Wayfern configs are sent. camoufoxConfig: null, wayfernConfig: mappedBrowser === "wayfern" ? wayfernConfig : null, }); toast.success( t("importProfile.importedSuccess", { name: newProfileName }), ); onClose(); } catch (error) { console.error("Failed to import profile:", error); const errorMessage = error instanceof Error ? error.message : String(error); if (parseBackendError(error)) { // Structured backend error (e.g. CAMOUFOX_IMPORT_DEPRECATED) — localize. toast.error(translateBackendError(t, error)); } else if (errorMessage.includes("No downloaded versions found")) { const browserDisplayName = getBrowserDisplayName(browserType); toast.error( t("importProfile.notInstalled", { browser: browserDisplayName }), { duration: 8000, }, ); } else { toast.error(t("importProfile.importFailed", { error: errorMessage })); } } finally { setIsImporting(false); } }, [ importMode, selectedDetectedProfile, autoDetectProfileName, detectedProfiles, manualBrowserType, manualProfilePath, manualProfileName, selectedProxyId, wayfernConfig, onClose, selectedProfile, t, ]); const handleClose = () => { setCurrentStep("select"); setWayfernConfig({}); setSelectedProxyId(undefined); setSelectedDetectedProfile(null); setAutoDetectProfileName(""); setManualBrowserType(null); setManualProfilePath(""); setManualProfileName(""); if (detectedProfiles.length > 0) { setImportMode("auto-detect"); } else { setImportMode("manual"); } onClose(); }; useEffect(() => { if (selectedDetectedProfile) { const profile = detectedProfiles.find( (p) => p.path === selectedDetectedProfile, ); if (profile) { const browserName = getBrowserDisplayName(profile.browser); const defaultName = `Old ${browserName}`; setAutoDetectProfileName(defaultName); } } }, [selectedDetectedProfile, detectedProfiles]); const currentMappedBrowser = useMemo(() => { if (importMode === "auto-detect" && selectedProfile) { return getMappedBrowser(selectedProfile.mapped_browser); } if (importMode === "manual" && manualBrowserType) { return getMappedBrowser(manualBrowserType); } return null; }, [importMode, selectedProfile, manualBrowserType]); const canProceedToNext = useMemo(() => { if (importMode === "auto-detect") { return ( !isLoading && !!selectedDetectedProfile && !!autoDetectProfileName.trim() ); } return ( !!manualBrowserType && !!manualProfilePath.trim() && !!manualProfileName.trim() ); }, [ importMode, isLoading, selectedDetectedProfile, autoDetectProfileName, manualBrowserType, manualProfilePath, manualProfileName, ]); useEffect(() => { if (isOpen) { void loadDetectedProfiles(); } }, [isOpen, loadDetectedProfiles]); return ( {!subPage && ( {t("importProfile.title")} )}
{currentStep === "select" && ( setImportMode(v as "auto-detect" | "manual") } className="flex flex-col gap-6" > {t("importProfile.autoDetect")} {t("importProfile.manualImport")}

{t("importProfile.detectedProfilesTitle")}

{isLoading ? (

{t("importProfile.scanning")}

) : detectedProfiles.length === 0 ? (

{t("importProfile.noneFound")}

{t("importProfile.noneFoundHint")}

) : (
{selectedProfile && (

{t("importProfile.pathLabel")} {" "} {selectedProfile.path}

{t("importProfile.browserLabel")} {" "} {getBrowserDisplayName(selectedProfile.browser)}

)}
{ setAutoDetectProfileName(e.target.value); }} placeholder={t( "importProfile.newProfileNamePlaceholder", )} />
)}

{t("importProfile.manualTitle")}

{ setManualProfilePath(e.target.value); }} placeholder={t( "importProfile.profileFolderPlaceholder", )} />

{t("importProfile.examplePaths")}
macOS: ~/Library/Application Support/Firefox/Profiles/xxx.default
Windows: %APPDATA%\Mozilla\Firefox\Profiles\xxx.default
Linux: ~/.mozilla/firefox/xxx.default

{ setManualProfileName(e.target.value); }} placeholder={t( "importProfile.newProfileNamePlaceholder", )} />
)} {currentStep === "configure" && currentMappedBrowser && (
{t("importProfile.importedAs", { browser: getBrowserDisplayName(currentMappedBrowser), })}
{/* Only Wayfern profiles are importable now (Camoufox/Firefox import is deprecated and blocked). */} { setWayfernConfig((prev) => ({ ...prev, [key]: value })); }} isCreating={true} crossOsUnlocked={crossOsUnlocked} limitedMode={!crossOsUnlocked} />
)}
{currentStep === "select" ? ( <> {!subPage && ( {t("common.buttons.cancel")} )} { setCurrentStep("configure"); }} > {t("importProfile.nextButton")} ) : ( <> { setCurrentStep("select"); }} > {t("common.buttons.back")} { void handleImport(); }} > {t("importProfile.importButton")} )}
); }