"use client"; import { invoke } from "@tauri-apps/api/core"; import { open } from "@tauri-apps/plugin-dialog"; import { useCallback, useEffect, useMemo, useState } from "react"; import { FaFolder } from "react-icons/fa"; import { toast } from "sonner"; import { LoadingButton } from "@/components/loading-button"; import { SharedCamoufoxConfigForm } from "@/components/shared-camoufox-config-form"; import { Alert, AlertDescription } from "@/components/ui/alert"; import { Button } from "@/components/ui/button"; import { Dialog, DialogContent, DialogFooter, 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 { getBrowserDisplayName, getBrowserIcon } from "@/lib/browser-utils"; import type { CamoufoxConfig, 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; } export function ImportProfileDialog({ isOpen, onClose, crossOsUnlocked, }: ImportProfileDialogProps) { 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 [camoufoxConfig, setCamoufoxConfig] = useState({}); 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(); const importableBrowsers = supportedBrowsers; 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("Failed to detect existing browser profiles"); } finally { setIsLoading(false); } }, []); const selectedProfile = detectedProfiles.find( (p) => p.path === selectedDetectedProfile, ); const handleBrowseFolder = async () => { try { const selected = await open({ directory: true, multiple: false, title: "Select Browser Profile Folder", }); if (selected && typeof selected === "string") { setManualProfilePath(selected); } } catch (error) { console.error("Failed to open folder dialog:", error); toast.error("Failed to open folder dialog"); } }; const handleImport = useCallback(async () => { let sourcePath: string; let browserType: string; let newProfileName: string; if (importMode === "auto-detect") { if (!selectedDetectedProfile || !autoDetectProfileName.trim()) { toast.error("Please select a profile and provide a name"); return; } const profile = detectedProfiles.find( (p) => p.path === selectedDetectedProfile, ); if (!profile) { toast.error("Selected profile not found"); return; } sourcePath = profile.path; browserType = profile.browser; newProfileName = autoDetectProfileName.trim(); } else { if ( !manualBrowserType || !manualProfilePath.trim() || !manualProfileName.trim() ) { toast.error("Please fill in all fields"); return; } sourcePath = manualProfilePath.trim(); browserType = manualBrowserType; newProfileName = manualProfileName.trim(); } const mappedBrowser = importMode === "auto-detect" && selectedProfile ? (selectedProfile.mapped_browser as "camoufox" | "wayfern") : getMappedBrowser(browserType); setIsImporting(true); try { await invoke("import_browser_profile", { sourcePath, browserType, newProfileName, proxyId: selectedProxyId ?? null, camoufoxConfig: mappedBrowser === "camoufox" ? camoufoxConfig : null, wayfernConfig: mappedBrowser === "wayfern" ? wayfernConfig : null, }); toast.success(`Successfully imported profile "${newProfileName}"`); onClose(); } catch (error) { console.error("Failed to import profile:", error); const errorMessage = error instanceof Error ? error.message : String(error); if (errorMessage.includes("No downloaded versions found")) { const browserDisplayName = getBrowserDisplayName(browserType); toast.error( `${browserDisplayName} is not installed. Please download ${browserDisplayName} first from the main window, then try importing again.`, { duration: 8000, }, ); } else { toast.error(`Failed to import profile: ${errorMessage}`); } } finally { setIsImporting(false); } }, [ importMode, selectedDetectedProfile, autoDetectProfileName, detectedProfiles, manualBrowserType, manualProfilePath, manualProfileName, selectedProxyId, camoufoxConfig, wayfernConfig, onClose, selectedProfile, ]); const handleClose = () => { setCurrentStep("select"); setCamoufoxConfig({}); 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 selectedProfile.mapped_browser as "camoufox" | "wayfern"; } if (importMode === "manual" && manualBrowserType) { return manualBrowserType as "camoufox" | "wayfern"; } 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 ( Import Browser Profile
{currentStep === "select" && ( <>
{ setImportMode("auto-detect"); }} className="flex-1" disabled={isLoading} > Auto-Detect { setImportMode("manual"); }} className="flex-1" disabled={isLoading} > Manual Import
{importMode === "auto-detect" && (

Detected Browser Profiles

{isLoading ? (

Scanning for browser profiles...

) : detectedProfiles.length === 0 ? (

No browser profiles found on your system.

Try the manual import option if you have profiles in custom locations.

) : (
{selectedProfile && (

Path:{" "} {selectedProfile.path}

Browser:{" "} {getBrowserDisplayName(selectedProfile.browser)}

)}
{ setAutoDetectProfileName(e.target.value); }} placeholder="Enter a name for the imported profile" />
)}
)} {importMode === "manual" && (

Manual Profile Import

{ setManualProfilePath(e.target.value); }} placeholder="Enter the full path to the profile folder" />

Example paths:
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="Enter a name for the imported profile" />
)} )} {currentStep === "configure" && currentMappedBrowser && (
This profile will be imported as a{" "} {getBrowserDisplayName(currentMappedBrowser)}{" "} profile.
{currentMappedBrowser === "camoufox" ? ( { setCamoufoxConfig((prev) => ({ ...prev, [key]: value })); }} isCreating={true} crossOsUnlocked={crossOsUnlocked} limitedMode={!crossOsUnlocked} /> ) : ( { setWayfernConfig((prev) => ({ ...prev, [key]: value })); }} isCreating={true} crossOsUnlocked={crossOsUnlocked} limitedMode={!crossOsUnlocked} /> )}
)}
{currentStep === "select" ? ( <> Cancel { setCurrentStep("configure"); }} > Next ) : ( <> { setCurrentStep("select"); }} > Back { void handleImport(); }} > Import )}
); }