"use client"; import { invoke } from "@tauri-apps/api/core"; import { useCallback, useEffect, useRef, useState } from "react"; import { LoadingButton } from "@/components/loading-button"; import { SharedCamoufoxConfigForm } from "@/components/shared-camoufox-config-form"; import { Button } from "@/components/ui/button"; import { Combobox } from "@/components/ui/combobox"; 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 { getBrowserIcon } from "@/lib/browser-utils"; import type { BrowserReleaseTypes, CamoufoxConfig, StoredProxy } from "@/types"; 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; description: string; } const browserOptions: BrowserOption[] = [ { value: "firefox", label: "Firefox", description: "Mozilla's main web browser", }, { value: "firefox-developer", label: "Firefox Developer Edition", description: "Browser for developers with cutting-edge features", }, { value: "chromium", label: "Chromium", description: "Open-source version of Chrome", }, { value: "brave", label: "Brave", description: "Privacy-focused browser with ad blocking", }, { value: "zen", label: "Zen Browser", description: "Beautiful, customizable Firefox-based browser", }, { value: "mullvad-browser", label: "Mullvad Browser", description: "TOR Browser fork by Mullvad VPN", }, { value: "tor-browser", label: "Tor Browser", description: "Browse anonymously through the Tor network", }, ]; export function CreateProfileDialog({ isOpen, onClose, onCreateProfile, selectedGroupId, }: CreateProfileDialogProps) { const [profileName, setProfileName] = useState(""); const [activeTab, setActiveTab] = useState("regular"); // Regular browser states const [selectedBrowser, setSelectedBrowser] = useState(); const [selectedProxyId, setSelectedProxyId] = useState(); const handleTabChange = (value: string) => { if (value === "regular") { setSelectedBrowser(undefined); } else if (value === "anti-detect") { setSelectedBrowser("camoufox"); } setActiveTab(value); }; // Camoufox anti-detect states const [camoufoxConfig, setCamoufoxConfig] = useState({ geoip: true, // Default to automatic geoip }); // Common states const [availableReleaseTypes, setAvailableReleaseTypes] = useState({}); const [camoufoxReleaseTypes, setCamoufoxReleaseTypes] = useState({}); const [supportedBrowsers, setSupportedBrowsers] = useState([]); const [storedProxies, setStoredProxies] = useState([]); const [isCreating, setIsCreating] = useState(false); 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 loadStoredProxies = useCallback(async () => { try { const proxies = await invoke("get_stored_proxies"); setStoredProxies(proxies); } catch (error) { console.error("Failed to load stored proxies:", error); } }, []); const loadReleaseTypes = useCallback( async (browser: string) => { // Set loading state loadingBrowserRef.current = browser; try { const releaseTypes = await invoke( "get_browser_release_types", { browserStr: browser }, ); // Only update state if this browser is still the one we're loading if (loadingBrowserRef.current === browser) { if (browser === "camoufox") { setCamoufoxReleaseTypes(releaseTypes); } else { setAvailableReleaseTypes(releaseTypes); } // Load downloaded versions for this browser await loadDownloadedVersions(browser); } } catch (error) { console.error(`Failed to load release types for ${browser}:`, error); } 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(); void loadStoredProxies(); // Load camoufox release types when dialog opens void loadReleaseTypes("camoufox"); } }, [isOpen, loadSupportedBrowsers, loadStoredProxies, loadReleaseTypes]); // 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 setAvailableReleaseTypes({}); void loadReleaseTypes(selectedBrowser); } }, [selectedBrowser, loadReleaseTypes]); // Helper function to get the best available version and release type const getBestAvailableVersion = useCallback( (releaseTypes: BrowserReleaseTypes, browserType?: string) => { // For Firefox Developer Edition, prefer nightly over stable if (browserType === "firefox-developer" && releaseTypes.nightly) { return { version: releaseTypes.nightly, releaseType: "nightly" as const, }; } if (releaseTypes.stable) { return { version: releaseTypes.stable, releaseType: "stable" as const }; } if (releaseTypes.nightly) { return { version: releaseTypes.nightly, releaseType: "nightly" as const, }; } return null; }, [], ); const handleDownload = async (browserStr: string) => { const releaseTypes = browserStr === "camoufox" ? camoufoxReleaseTypes : availableReleaseTypes; const bestVersion = getBestAvailableVersion(releaseTypes, 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 === "regular") { if (!selectedBrowser) { console.error("Missing required browser selection"); return; } // Use the best available version (stable preferred, nightly as fallback) const bestVersion = getBestAvailableVersion( availableReleaseTypes, 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, }); } else { // Anti-detect tab - always use Camoufox with best available version const bestCamoufoxVersion = getBestAvailableVersion( camoufoxReleaseTypes, "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, }); } 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(""); setSelectedBrowser(undefined); setSelectedProxyId(undefined); setAvailableReleaseTypes({}); setCamoufoxReleaseTypes({}); setCamoufoxConfig({ geoip: true, // Reset to automatic geoip }); setActiveTab("regular"); onClose(); }; const isCreateDisabled = () => { if (!profileName.trim()) return true; if (!selectedBrowser) return true; if (isBrowserCurrentlyDownloading(selectedBrowser)) return true; if (!isBrowserVersionAvailable(selectedBrowser)) return true; if (selectedBrowser === "camoufox") { return !getBestAvailableVersion(camoufoxReleaseTypes, selectedBrowser); } return !getBestAvailableVersion(availableReleaseTypes, selectedBrowser); }; const updateCamoufoxConfig = (key: keyof CamoufoxConfig, value: unknown) => { setCamoufoxConfig((prev) => ({ ...prev, [key]: value })); }; // Check if browser version is downloaded and available const isBrowserVersionAvailable = (browserStr: string) => { const releaseTypes = browserStr === "camoufox" ? camoufoxReleaseTypes : availableReleaseTypes; const bestVersion = getBestAvailableVersion(releaseTypes, browserStr); return bestVersion && isVersionDownloaded(bestVersion.version); }; // Check if browser is currently downloading const isBrowserCurrentlyDownloading = (browserStr: string) => { return isBrowserDownloading(browserStr); }; console.log(selectedBrowser); return ( Create New Profile Regular Browsers Anti-Detect
{/* Profile Name - Common to both tabs */}
setProfileName(e.target.value)} placeholder="Enter profile name" />
supportedBrowsers.includes(browser.value), ) .map((browser) => { const IconComponent = getBrowserIcon(browser.value); return { value: browser.value, label: browser.label, icon: IconComponent, }; })} value={selectedBrowser || ""} onValueChange={(value) => setSelectedBrowser(value as BrowserTypeString) } placeholder="Select a browser..." searchPlaceholder="Search browsers..." />
{selectedBrowser && (
{!isBrowserCurrentlyDownloading(selectedBrowser) && !isBrowserVersionAvailable(selectedBrowser) && getBestAvailableVersion( availableReleaseTypes, selectedBrowser, ) && (

{(() => { const bestVersion = getBestAvailableVersion( availableReleaseTypes, selectedBrowser, ); return `${bestVersion?.releaseType === "stable" ? "Latest stable" : "Latest nightly"} version (${bestVersion?.version}) needs to be downloaded`; })()}

handleDownload(selectedBrowser)} isLoading={isBrowserCurrentlyDownloading( selectedBrowser, )} size="sm" disabled={isBrowserCurrentlyDownloading( selectedBrowser, )} > Download
)} {!isBrowserCurrentlyDownloading(selectedBrowser) && isBrowserVersionAvailable(selectedBrowser) && (
{(() => { const bestVersion = getBestAvailableVersion( availableReleaseTypes, selectedBrowser, ); return `✓ ${bestVersion?.releaseType === "stable" ? "Latest stable" : "Latest nightly"} version (${bestVersion?.version}) is available`; })()}
)} {isBrowserCurrentlyDownloading(selectedBrowser) && (
{(() => { const bestVersion = getBestAvailableVersion( availableReleaseTypes, selectedBrowser, ); return `Downloading ${bestVersion?.releaseType === "stable" ? "stable" : "nightly"} version (${bestVersion?.version})...`; })()}
)}
)}
{/* Camoufox Download Status */} {!isBrowserCurrentlyDownloading("camoufox") && !isBrowserVersionAvailable("camoufox") && getBestAvailableVersion( camoufoxReleaseTypes, "camoufox", ) && (

{(() => { const bestVersion = getBestAvailableVersion( camoufoxReleaseTypes, "camoufox", ); return `Camoufox ${bestVersion?.releaseType} 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( camoufoxReleaseTypes, "camoufox", ); return `✓ Camoufox ${bestVersion?.releaseType} version (${bestVersion?.version}) is available`; })()}
)} {isBrowserCurrentlyDownloading("camoufox") && (
{(() => { const bestVersion = getBestAvailableVersion( camoufoxReleaseTypes, "camoufox", ); return `Downloading Camoufox ${bestVersion?.releaseType} version (${bestVersion?.version})...`; })()}
)}
{/* Proxy Selection - Common to both tabs - Compact without card */} {storedProxies.length > 0 && (
)}
Create Profile
); }