"use client"; import { invoke } from "@tauri-apps/api/core"; import { emit } from "@tauri-apps/api/event"; import { Loader2 } from "lucide-react"; import { useCallback, useEffect, useState } from "react"; import { toast } from "sonner"; import { Button } from "@/components/ui/button"; import { Combobox } from "@/components/ui/combobox"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import type { LocationItem } from "@/types"; import { RippleButton } from "./ui/ripple"; interface LocationProxyDialogProps { isOpen: boolean; onClose: () => void; } export function LocationProxyDialog({ isOpen, onClose, }: LocationProxyDialogProps) { const [countries, setCountries] = useState([]); const [regions, setRegions] = useState([]); const [cities, setCities] = useState([]); const [isps, setIsps] = useState([]); const [selectedCountry, setSelectedCountry] = useState(""); const [selectedRegion, setSelectedRegion] = useState(""); const [selectedCity, setSelectedCity] = useState(""); const [selectedIsp, setSelectedIsp] = useState(""); const [proxyName, setProxyName] = useState(""); const [isLoadingCountries, setIsLoadingCountries] = useState(false); const [isLoadingRegions, setIsLoadingRegions] = useState(false); const [isLoadingCities, setIsLoadingCities] = useState(false); const [isLoadingIsps, setIsLoadingIsps] = useState(false); const [isCreating, setIsCreating] = useState(false); const handleClose = useCallback(() => { setSelectedCountry(""); setSelectedRegion(""); setSelectedCity(""); setSelectedIsp(""); setProxyName(""); setRegions([]); setCities([]); setIsps([]); onClose(); }, [onClose]); // Fetch countries on mount useEffect(() => { if (!isOpen) return; setIsLoadingCountries(true); void invoke("cloud_get_countries") .then((data) => { setCountries(data); }) .catch((err) => { console.error("Failed to fetch countries:", err); toast.error("Failed to load countries"); }) .finally(() => { setIsLoadingCountries(false); }); }, [isOpen]); // Fetch regions when country changes useEffect(() => { if (!selectedCountry) { setRegions([]); return; } setIsLoadingRegions(true); setSelectedRegion(""); setSelectedCity(""); setSelectedIsp(""); setCities([]); setIsps([]); void invoke("cloud_get_regions", { country: selectedCountry, }) .then((data) => { setRegions(data); }) .catch((err) => { console.error("Failed to fetch regions:", err); }) .finally(() => { setIsLoadingRegions(false); }); }, [selectedCountry]); // Fetch cities when country or region changes (cities can be loaded without region) useEffect(() => { if (!selectedCountry) { setCities([]); return; } setIsLoadingCities(true); setSelectedCity(""); const args: { country: string; region?: string } = { country: selectedCountry, }; if (selectedRegion) { args.region = selectedRegion; } void invoke("cloud_get_cities", args) .then((data) => { setCities(data); }) .catch((err) => { console.error("Failed to fetch cities:", err); }) .finally(() => { setIsLoadingCities(false); }); }, [selectedCountry, selectedRegion]); // Fetch ISPs when country/region/city changes useEffect(() => { if (!selectedCountry) { setIsps([]); return; } setIsLoadingIsps(true); setSelectedIsp(""); const args: { country: string; region?: string; city?: string } = { country: selectedCountry, }; if (selectedRegion) args.region = selectedRegion; if (selectedCity) args.city = selectedCity; void invoke("cloud_get_isps", args) .then((data) => { setIsps(data); }) .catch((err) => { console.error("Failed to fetch ISPs:", err); }) .finally(() => { setIsLoadingIsps(false); }); }, [selectedCountry, selectedRegion, selectedCity]); // Auto-generate name from selections useEffect(() => { const parts: string[] = []; const countryItem = countries.find((c) => c.code === selectedCountry); if (countryItem) parts.push(countryItem.name); const regionItem = regions.find((s) => s.code === selectedRegion); if (regionItem) parts.push(regionItem.name); const cityItem = cities.find((c) => c.code === selectedCity); if (cityItem) parts.push(cityItem.name); const ispItem = isps.find((i) => i.code === selectedIsp); if (ispItem) parts.push(ispItem.name); if (parts.length > 0) { setProxyName(parts.join(" - ")); } }, [ selectedCountry, selectedRegion, selectedCity, selectedIsp, countries, regions, cities, isps, ]); const handleCreate = useCallback(async () => { if (!selectedCountry || !proxyName.trim()) return; setIsCreating(true); try { await invoke("create_cloud_location_proxy", { name: proxyName.trim(), country: selectedCountry, region: selectedRegion || null, city: selectedCity || null, isp: selectedIsp || null, }); toast.success("Location proxy created"); await emit("stored-proxies-changed"); handleClose(); } catch (error) { console.error("Failed to create location proxy:", error); toast.error( typeof error === "string" ? error : "Failed to create location proxy", ); } finally { setIsCreating(false); } }, [ selectedCountry, selectedRegion, selectedCity, selectedIsp, proxyName, handleClose, ]); const countryOptions = countries.map((c) => ({ value: c.code, label: c.name, })); const regionOptions = regions.map((s) => ({ value: s.code, label: s.name })); const cityOptions = cities.map((c) => ({ value: c.code, label: c.name })); const ispOptions = isps.map((i) => ({ value: i.code, label: i.name })); const LoadingSpinner = () => ( ); return ( Create Location Proxy Create a geo-targeted proxy with a 24-hour sticky session
{/* Country - always visible */}
{/* Region - always visible, disabled until country is selected */}
{/* City - always visible, disabled until country is selected */}
{/* ISP - always visible, disabled until country is selected */}
{/* Name */}
{ setProxyName(e.target.value); }} placeholder="Proxy name" />
{isCreating ? "Creating..." : "Create"}
); }