From bc7c8d1a1e4cc74eb5511fe4bdc99b247e788f61 Mon Sep 17 00:00:00 2001 From: zhom <2717306+zhom@users.noreply.github.com> Date: Mon, 1 Sep 2025 14:40:55 +0400 Subject: [PATCH] refactor: better profile creation flow --- src/components/create-profile-dialog.tsx | 707 +++++++++++++++-------- 1 file changed, 476 insertions(+), 231 deletions(-) diff --git a/src/components/create-profile-dialog.tsx b/src/components/create-profile-dialog.tsx index 90aebd1..97d702c 100644 --- a/src/components/create-profile-dialog.tsx +++ b/src/components/create-profile-dialog.tsx @@ -6,7 +6,7 @@ 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 { Combobox } from "@/components/ui/combobox"; +import { Button } from "@/components/ui/button"; import { Dialog, DialogContent, @@ -25,6 +25,7 @@ import { 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"; @@ -99,28 +100,43 @@ export function CreateProfileDialog({ selectedGroupId, }: CreateProfileDialogProps) { const [profileName, setProfileName] = useState(""); + const [currentStep, setCurrentStep] = useState< + "browser-selection" | "browser-config" + >("browser-selection"); const [activeTab, setActiveTab] = useState("anti-detect"); - // Regular browser states + // Browser selection states const [selectedBrowser, setSelectedBrowser] = - useState("camoufox"); + useState(null); const [selectedProxyId, setSelectedProxyId] = useState(); - const handleTabChange = (value: string) => { - if (value === "regular") { - setSelectedBrowser("firefox"); - } else if (value === "anti-detect") { - setSelectedBrowser("camoufox"); - } - - setActiveTab(value); - }; - // 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); @@ -227,15 +243,15 @@ export function CreateProfileDialog({ // Load data when dialog opens useEffect(() => { if (isOpen) { - // Ensure we have a selected browser - if (!selectedBrowser) { - setSelectedBrowser("camoufox"); - } void loadSupportedBrowsers(); - // Load camoufox release types when dialog opens - void loadReleaseTypes(selectedBrowser || "camoufox"); + // Load release types when a browser is selected + if (selectedBrowser) { + void loadReleaseTypes(selectedBrowser); + } // Check and download GeoIP database if needed for Camoufox - void checkAndDownloadGeoIPDatabase(); + if (selectedBrowser === "camoufox") { + void checkAndDownloadGeoIPDatabase(); + } } }, [ isOpen, @@ -297,7 +313,29 @@ export function CreateProfileDialog({ setIsCreating(true); try { - if (activeTab === "regular") { + 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; @@ -318,27 +356,6 @@ export function CreateProfileDialog({ proxyId: selectedProxyId, groupId: selectedGroupId !== "default" ? selectedGroupId : undefined, }); - } else { - // Anti-detect tab - 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, - }); } handleClose(); @@ -355,13 +372,14 @@ export function CreateProfileDialog({ // Reset all states setProfileName(""); - setSelectedBrowser("camoufox"); // Set default browser instead of null + setCurrentStep("browser-selection"); + setActiveTab("anti-detect"); + setSelectedBrowser(null); setSelectedProxyId(undefined); setReleaseTypes({}); setCamoufoxConfig({ geoip: true, // Reset to automatic geoip }); - setActiveTab("anti-detect"); onClose(); }; @@ -400,11 +418,23 @@ export function CreateProfileDialog({ 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 ( - Create New Profile + + {currentStep === "browser-selection" + ? "Create New Profile" + : "Configure Profile"} + Regular - +
- {/* Profile Name - Common to both tabs */} -
- - setProfileName(e.target.value)} - placeholder="Enter profile name" - /> -
+ {currentStep === "browser-selection" ? ( + <> + + {/* Anti-Detect Browser Selection */} +
+
+

+ Anti-Detect Browser +

+

+ Choose Firefox for anti-detection capabilities +

+
- -
-
- - - supportedBrowsers.includes(browser.value) && - browser.value !== "mullvad-browser" && - browser.value !== "tor-browser", - ) - .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(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) && ( -
+
- )} -
- + - -
- {/* Camoufox Download Status */} - {!isBrowserCurrentlyDownloading("camoufox") && - !isBrowserVersionAvailable("camoufox") && - getBestAvailableVersion("camoufox") && ( -
-

- {(() => { - const bestVersion = - getBestAvailableVersion("camoufox"); - return `Camoufox version (${bestVersion?.version}) needs to be downloaded`; - })()} + + {/* Regular Browser Selection */} +

+
+

+ Regular Browsers +

+

+ Choose from supported regular browsers

- 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`; - })()} + +
+ {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 ( + + ); + })}
- )} - {isBrowserCurrentlyDownloading("camoufox") && ( -
- {(() => { - const bestVersion = - getBestAvailableVersion("camoufox"); - return `Downloading Camoufox version (${bestVersion?.version})...`; - })()}
- )} + + + ) : ( + <> + + {/* 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})...`; + })()} +
+ )} - {/* Proxy Selection - Common to both tabs - 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 +
+ {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 - - Create - - - + )} +