"use client"; import { invoke } from "@tauri-apps/api/core"; import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { GoPlus } from "react-icons/go"; import { LoadingButton } from "@/components/loading-button"; import { ProxyFormDialog } from "@/components/proxy-form-dialog"; import { SharedCamoufoxConfigForm } from "@/components/shared-camoufox-config-form"; 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 { ScrollArea } from "@/components/ui/scroll-area"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { useBrowserDownload } from "@/hooks/use-browser-download"; import { useProxyEvents } from "@/hooks/use-proxy-events"; import { getBrowserIcon } from "@/lib/browser-utils"; import type { BrowserReleaseTypes, CamoufoxConfig } from "@/types"; import { RippleButton } from "./ui/ripple"; type BrowserTypeString = | "mullvad-browser" | "firefox" | "firefox-developer" | "chromium" | "brave" | "zen" | "tor-browser" | "camoufox"; interface CreateProfileDialogProps { isOpen: boolean; onClose: () => void; onCreateProfile: (profileData: { name: string; browserStr: BrowserTypeString; version: string; releaseType: string; proxyId?: string; camoufoxConfig?: CamoufoxConfig; groupId?: string; }) => Promise; selectedGroupId?: string; } interface BrowserOption { value: BrowserTypeString; label: string; } const browserOptions: BrowserOption[] = [ { value: "firefox", label: "Firefox", }, { value: "firefox-developer", label: "Firefox Developer Edition", }, { value: "chromium", label: "Chromium", }, { value: "brave", label: "Brave", }, { value: "zen", label: "Zen Browser", }, { value: "mullvad-browser", label: "Mullvad Browser", }, { value: "tor-browser", label: "Tor Browser", }, ]; export function CreateProfileDialog({ isOpen, onClose, onCreateProfile, selectedGroupId, }: CreateProfileDialogProps) { const [profileName, setProfileName] = useState(""); const [currentStep, setCurrentStep] = useState< "browser-selection" | "browser-config" >("browser-selection"); const [activeTab, setActiveTab] = useState("anti-detect"); // Browser selection states const [selectedBrowser, setSelectedBrowser] = useState(null); const [selectedProxyId, setSelectedProxyId] = useState(); // Camoufox anti-detect states const [camoufoxConfig, setCamoufoxConfig] = useState({ geoip: true, // Default to automatic geoip }); // Handle browser selection from the initial screen const handleBrowserSelect = (browser: BrowserTypeString) => { setSelectedBrowser(browser); setCurrentStep("browser-config"); }; // Handle back button const handleBack = () => { setCurrentStep("browser-selection"); setSelectedBrowser(null); setProfileName(""); setSelectedProxyId(undefined); }; const handleTabChange = (value: string) => { setActiveTab(value); setCurrentStep("browser-selection"); setSelectedBrowser(null); setProfileName(""); setSelectedProxyId(undefined); }; const [supportedBrowsers, setSupportedBrowsers] = useState([]); const { storedProxies } = useProxyEvents(); const [showProxyForm, setShowProxyForm] = useState(false); const [isCreating, setIsCreating] = useState(false); const [releaseTypes, setReleaseTypes] = useState(); const loadingBrowserRef = useRef(null); // Use the browser download hook const { isBrowserDownloading, downloadBrowser, loadDownloadedVersions, isVersionDownloaded, } = useBrowserDownload(); const loadSupportedBrowsers = useCallback(async () => { try { const browsers = await invoke("get_supported_browsers"); setSupportedBrowsers(browsers); } catch (error) { console.error("Failed to load supported browsers:", error); } }, []); const checkAndDownloadGeoIPDatabase = useCallback(async () => { try { const isAvailable = await invoke("is_geoip_database_available"); if (!isAvailable) { console.log("GeoIP database not available, downloading..."); await invoke("download_geoip_database"); console.log("GeoIP database downloaded successfully"); } } catch (error) { console.error("Failed to check/download GeoIP database:", error); // Don't show error to user as this is not critical for profile creation } }, []); const loadReleaseTypes = useCallback( async (browser: string) => { // Set loading state loadingBrowserRef.current = browser; try { const rawReleaseTypes = await invoke( "get_browser_release_types", { browserStr: browser }, ); await loadDownloadedVersions(browser); // Only update state if this browser is still the one we're loading if (loadingBrowserRef.current === browser) { // Filter to enforce stable-only creation, except Firefox Developer (nightly-only) if (browser === "camoufox") { const filtered: BrowserReleaseTypes = {}; if (rawReleaseTypes.stable) filtered.stable = rawReleaseTypes.stable; setReleaseTypes(filtered); } else if (browser === "firefox-developer") { const filtered: BrowserReleaseTypes = {}; if (rawReleaseTypes.nightly) filtered.nightly = rawReleaseTypes.nightly; setReleaseTypes(filtered); } else { const filtered: BrowserReleaseTypes = {}; if (rawReleaseTypes.stable) filtered.stable = rawReleaseTypes.stable; setReleaseTypes(filtered); } } } catch (error) { console.error(`Failed to load release types for ${browser}:`, error); // Fallback: still load downloaded versions and derive release type from them if possible try { const downloaded = await loadDownloadedVersions(browser); if (loadingBrowserRef.current === browser && downloaded.length > 0) { const latest = downloaded[0]; const fallback: BrowserReleaseTypes = {}; if (browser === "firefox-developer") { fallback.nightly = latest; } else { fallback.stable = latest; } setReleaseTypes(fallback); } } catch (e) { console.error( `Failed to load downloaded versions for ${browser}:`, e, ); } } finally { // Clear loading state only if we're still loading this browser if (loadingBrowserRef.current === browser) { loadingBrowserRef.current = null; } } }, [loadDownloadedVersions], ); // Load data when dialog opens useEffect(() => { if (isOpen) { void loadSupportedBrowsers(); // Load release types when a browser is selected if (selectedBrowser) { void loadReleaseTypes(selectedBrowser); } // Check and download GeoIP database if needed for Camoufox if (selectedBrowser === "camoufox") { void checkAndDownloadGeoIPDatabase(); } } }, [ isOpen, loadSupportedBrowsers, loadReleaseTypes, checkAndDownloadGeoIPDatabase, selectedBrowser, ]); // Load release types when browser selection changes useEffect(() => { if (selectedBrowser) { // Cancel any previous loading loadingBrowserRef.current = null; // Clear previous release types immediately to prevent showing stale data setReleaseTypes({}); void loadReleaseTypes(selectedBrowser); } }, [selectedBrowser, loadReleaseTypes]); // Helper function to get the best available version respecting rules const getBestAvailableVersion = useCallback( (browserType?: string) => { if (!releaseTypes) return null; // Firefox Developer Edition: nightly-only if (browserType === "firefox-developer" && releaseTypes.nightly) { return { version: releaseTypes.nightly, releaseType: "nightly" as const, }; } // All others: stable-only if (releaseTypes.stable) { return { version: releaseTypes.stable, releaseType: "stable" as const }; } return null; }, [releaseTypes], ); const handleDownload = async (browserStr: string) => { const bestVersion = getBestAvailableVersion(browserStr); if (!bestVersion) { console.error("No version available for download"); return; } try { await downloadBrowser(browserStr, bestVersion.version); } catch (error) { console.error("Failed to download browser:", error); } }; const handleCreate = async () => { if (!profileName.trim()) return; setIsCreating(true); try { if (activeTab === "anti-detect") { // Anti-detect browser - always use Camoufox with best available version const bestCamoufoxVersion = getBestAvailableVersion("camoufox"); if (!bestCamoufoxVersion) { console.error("No Camoufox version available"); return; } // The fingerprint will be generated at launch time by the Rust backend // We don't need to generate it here during profile creation const finalCamoufoxConfig = { ...camoufoxConfig }; await onCreateProfile({ name: profileName.trim(), browserStr: "camoufox" as BrowserTypeString, version: bestCamoufoxVersion.version, releaseType: bestCamoufoxVersion.releaseType, proxyId: selectedProxyId, camoufoxConfig: finalCamoufoxConfig, groupId: selectedGroupId !== "default" ? selectedGroupId : undefined, }); } else { // Regular browser if (!selectedBrowser) { console.error("Missing required browser selection"); return; } // Use the best available version (stable preferred, nightly as fallback) const bestVersion = getBestAvailableVersion(selectedBrowser); if (!bestVersion) { console.error("No version available"); return; } await onCreateProfile({ name: profileName.trim(), browserStr: selectedBrowser, version: bestVersion.version, releaseType: bestVersion.releaseType, proxyId: selectedProxyId, groupId: selectedGroupId !== "default" ? selectedGroupId : undefined, }); } handleClose(); } catch (error) { console.error("Failed to create profile:", error); } finally { setIsCreating(false); } }; const handleClose = () => { // Cancel any ongoing loading loadingBrowserRef.current = null; // Reset all states setProfileName(""); setCurrentStep("browser-selection"); setActiveTab("anti-detect"); setSelectedBrowser(null); setSelectedProxyId(undefined); setReleaseTypes({}); setCamoufoxConfig({ geoip: true, // Reset to automatic geoip }); onClose(); }; const updateCamoufoxConfig = (key: keyof CamoufoxConfig, value: unknown) => { setCamoufoxConfig((prev) => ({ ...prev, [key]: value })); }; // Check if browser version is downloaded and available const isBrowserVersionAvailable = useCallback( (browserStr: string) => { const bestVersion = getBestAvailableVersion(browserStr); return bestVersion && isVersionDownloaded(bestVersion.version); }, [isVersionDownloaded, getBestAvailableVersion], ); // Check if browser is currently downloading const isBrowserCurrentlyDownloading = useCallback( (browserStr: string) => { return isBrowserDownloading(browserStr); }, [isBrowserDownloading], ); const isCreateDisabled = useMemo(() => { if (!profileName.trim()) return true; if (!selectedBrowser) return true; if (isBrowserCurrentlyDownloading(selectedBrowser)) return true; if (!isBrowserVersionAvailable(selectedBrowser)) return true; return false; }, [ profileName, selectedBrowser, isBrowserCurrentlyDownloading, isBrowserVersionAvailable, ]); // Filter supported browsers for regular browsers (excluding mullvad and tor) const regularBrowsers = browserOptions.filter( (browser) => supportedBrowsers.includes(browser.value) && browser.value !== "mullvad-browser" && browser.value !== "tor-browser", ); return ( {currentStep === "browser-selection" ? "Create New Profile" : "Configure Profile"} Anti-Detect Regular
{currentStep === "browser-selection" ? ( <> {/* Anti-Detect Browser Selection */}

Anti-Detect Browser

Choose Firefox for anti-detection capabilities

{/* Regular Browser Selection */}

Regular Browsers

Choose from supported regular browsers

{regularBrowsers.map((browser) => { if (browser.value === "camoufox") return null; // Skip camoufox as it's handled in anti-detect tab const IconComponent = getBrowserIcon(browser.value); return ( ); })}
) : ( <> {/* Anti-Detect Configuration */}
{/* Profile Name */}
setProfileName(e.target.value)} placeholder="Enter profile name" />
{selectedBrowser === "camoufox" ? ( // Camoufox Configuration
{/* Camoufox Download Status */} {!isBrowserCurrentlyDownloading("camoufox") && !isBrowserVersionAvailable("camoufox") && getBestAvailableVersion("camoufox") && (

{(() => { const bestVersion = getBestAvailableVersion("camoufox"); return `Camoufox version (${bestVersion?.version}) needs to be downloaded`; })()}

handleDownload("camoufox")} isLoading={isBrowserCurrentlyDownloading( "camoufox", )} size="sm" disabled={isBrowserCurrentlyDownloading( "camoufox", )} > {isBrowserCurrentlyDownloading("camoufox") ? "Downloading..." : "Download"}
)} {!isBrowserCurrentlyDownloading("camoufox") && isBrowserVersionAvailable("camoufox") && (
{(() => { const bestVersion = getBestAvailableVersion("camoufox"); return `✓ Camoufox version (${bestVersion?.version}) is available`; })()}
)} {isBrowserCurrentlyDownloading("camoufox") && (
{(() => { const bestVersion = getBestAvailableVersion("camoufox"); return `Downloading Camoufox version (${bestVersion?.version})...`; })()}
)}
) : ( // Regular Browser Configuration
{selectedBrowser && (
{!isBrowserCurrentlyDownloading( selectedBrowser, ) && !isBrowserVersionAvailable(selectedBrowser) && getBestAvailableVersion(selectedBrowser) && (

{(() => { const bestVersion = getBestAvailableVersion( selectedBrowser, ); return `Latest version (${bestVersion?.version}) needs to be downloaded`; })()}

handleDownload(selectedBrowser) } isLoading={isBrowserCurrentlyDownloading( selectedBrowser, )} className="ml-auto" size="sm" disabled={isBrowserCurrentlyDownloading( selectedBrowser, )} > Download
)} {!isBrowserCurrentlyDownloading( selectedBrowser, ) && isBrowserVersionAvailable( selectedBrowser, ) && (
{(() => { const bestVersion = getBestAvailableVersion( selectedBrowser, ); return `✓ Latest version (${bestVersion?.version}) is available`; })()}
)} {isBrowserCurrentlyDownloading( selectedBrowser, ) && (
{(() => { const bestVersion = getBestAvailableVersion( selectedBrowser, ); return `Downloading version (${bestVersion?.version})...`; })()}
)}
)}
)} {/* Proxy Selection - Always visible */}
setShowProxyForm(true)} className="px-2 h-7 text-xs" > Add Proxy
{storedProxies.length > 0 ? ( ) : (
No proxies available. Add one to route this profile's traffic.
)}
{/* Regular Browser Configuration */}
{/* Profile Name */}
setProfileName(e.target.value)} placeholder="Enter profile name" />
{/* Regular Browser Configuration */}
{selectedBrowser && (
{!isBrowserCurrentlyDownloading( selectedBrowser, ) && !isBrowserVersionAvailable(selectedBrowser) && getBestAvailableVersion(selectedBrowser) && (

{(() => { const bestVersion = getBestAvailableVersion( selectedBrowser, ); return `Latest version (${bestVersion?.version}) needs to be downloaded`; })()}

handleDownload(selectedBrowser) } isLoading={isBrowserCurrentlyDownloading( selectedBrowser, )} className="ml-auto" size="sm" disabled={isBrowserCurrentlyDownloading( selectedBrowser, )} > Download
)} {!isBrowserCurrentlyDownloading( selectedBrowser, ) && isBrowserVersionAvailable(selectedBrowser) && (
{(() => { const bestVersion = getBestAvailableVersion( selectedBrowser, ); return `✓ Latest version (${bestVersion?.version}) is available`; })()}
)} {isBrowserCurrentlyDownloading( selectedBrowser, ) && (
{(() => { const bestVersion = getBestAvailableVersion(selectedBrowser); return `Downloading version (${bestVersion?.version})...`; })()}
)}
)}
{/* Proxy Selection - Always visible */}
setShowProxyForm(true)} className="px-2 h-7 text-xs" > Add Proxy
{storedProxies.length > 0 ? ( ) : (
No proxies available. Add one to route this profile's traffic.
)}
)}
{currentStep === "browser-config" ? ( <> Back Create ) : ( Cancel )}
setShowProxyForm(false)} />
); }