refactor: cleanup

This commit is contained in:
zhom
2026-03-09 14:24:18 +04:00
parent a8be96d28e
commit 43ee6856f9
47 changed files with 5619 additions and 1535 deletions
+160 -52
View File
@@ -2,6 +2,7 @@
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";
@@ -29,26 +30,31 @@ export function LocationProxyDialog({
onClose,
}: LocationProxyDialogProps) {
const [countries, setCountries] = useState<LocationItem[]>([]);
const [states, setStates] = useState<LocationItem[]>([]);
const [regions, setRegions] = useState<LocationItem[]>([]);
const [cities, setCities] = useState<LocationItem[]>([]);
const [isps, setIsps] = useState<LocationItem[]>([]);
const [selectedCountry, setSelectedCountry] = useState("");
const [selectedState, setSelectedState] = useState("");
const [selectedRegion, setSelectedRegion] = useState("");
const [selectedCity, setSelectedCity] = useState("");
const [selectedIsp, setSelectedIsp] = useState("");
const [proxyName, setProxyName] = useState("");
const [isLoadingCountries, setIsLoadingCountries] = useState(false);
const [isLoadingStates, setIsLoadingStates] = 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("");
setSelectedState("");
setSelectedRegion("");
setSelectedCity("");
setSelectedIsp("");
setProxyName("");
setStates([]);
setRegions([]);
setCities([]);
setIsps([]);
onClose();
}, [onClose]);
@@ -65,52 +71,87 @@ export function LocationProxyDialog({
.finally(() => setIsLoadingCountries(false));
}, [isOpen]);
// Fetch states when country changes
// Fetch regions when country changes
useEffect(() => {
if (!selectedCountry) {
setStates([]);
setRegions([]);
return;
}
setIsLoadingStates(true);
setSelectedState("");
setIsLoadingRegions(true);
setSelectedRegion("");
setSelectedCity("");
setSelectedIsp("");
setCities([]);
invoke<LocationItem[]>("cloud_get_states", { country: selectedCountry })
.then((data) => setStates(data))
.catch((err) => console.error("Failed to fetch states:", err))
.finally(() => setIsLoadingStates(false));
setIsps([]);
invoke<LocationItem[]>("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 state changes
// Fetch cities when country or region changes (cities can be loaded without region)
useEffect(() => {
if (!selectedCountry || !selectedState) {
if (!selectedCountry) {
setCities([]);
return;
}
setIsLoadingCities(true);
setSelectedCity("");
invoke<LocationItem[]>("cloud_get_cities", {
const args: { country: string; region?: string } = {
country: selectedCountry,
state: selectedState,
})
};
if (selectedRegion) {
args.region = selectedRegion;
}
invoke<LocationItem[]>("cloud_get_cities", args)
.then((data) => setCities(data))
.catch((err) => console.error("Failed to fetch cities:", err))
.finally(() => setIsLoadingCities(false));
}, [selectedCountry, selectedState]);
}, [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;
invoke<LocationItem[]>("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 stateItem = states.find((s) => s.code === selectedState);
if (stateItem) parts.push(stateItem.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, selectedState, selectedCity, countries, states, cities]);
}, [
selectedCountry,
selectedRegion,
selectedCity,
selectedIsp,
countries,
regions,
cities,
isps,
]);
const handleCreate = useCallback(async () => {
if (!selectedCountry || !proxyName.trim()) return;
@@ -119,8 +160,9 @@ export function LocationProxyDialog({
await invoke("create_cloud_location_proxy", {
name: proxyName.trim(),
country: selectedCountry,
state: selectedState || null,
region: selectedRegion || null,
city: selectedCity || null,
isp: selectedIsp || null,
});
toast.success("Location proxy created");
await emit("stored-proxies-changed");
@@ -133,14 +175,26 @@ export function LocationProxyDialog({
} finally {
setIsCreating(false);
}
}, [selectedCountry, selectedState, selectedCity, proxyName, handleClose]);
}, [
selectedCountry,
selectedRegion,
selectedCity,
selectedIsp,
proxyName,
handleClose,
]);
const countryOptions = countries.map((c) => ({
value: c.code,
label: c.name,
}));
const stateOptions = states.map((s) => ({ value: s.code, label: s.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 = () => (
<Loader2 className="h-4 w-4 animate-spin text-muted-foreground" />
);
return (
<Dialog open={isOpen} onOpenChange={handleClose}>
@@ -148,48 +202,102 @@ export function LocationProxyDialog({
<DialogHeader>
<DialogTitle>Create Location Proxy</DialogTitle>
<DialogDescription>
Create a geo-targeted proxy from your cloud credentials
Create a geo-targeted proxy with a 24-hour sticky session
</DialogDescription>
</DialogHeader>
<div className="space-y-4">
{/* Country - always visible */}
<div className="space-y-2">
<Label>Country (required)</Label>
<Label className="flex items-center gap-2">
Country (required)
{isLoadingCountries && <LoadingSpinner />}
</Label>
<Combobox
options={countryOptions}
value={selectedCountry}
onValueChange={setSelectedCountry}
placeholder={isLoadingCountries ? "Loading..." : "Select country"}
placeholder={
isLoadingCountries ? "Loading countries..." : "Select country"
}
searchPlaceholder="Search countries..."
disabled={isLoadingCountries}
/>
</div>
{selectedCountry && stateOptions.length > 0 && (
<div className="space-y-2">
<Label>State (optional)</Label>
<Combobox
options={stateOptions}
value={selectedState}
onValueChange={setSelectedState}
placeholder={isLoadingStates ? "Loading..." : "Select state"}
searchPlaceholder="Search states..."
/>
</div>
)}
{/* Region - always visible, disabled until country is selected */}
<div className="space-y-2">
<Label className="flex items-center gap-2">
Region (optional)
{isLoadingRegions && <LoadingSpinner />}
</Label>
<Combobox
options={regionOptions}
value={selectedRegion}
onValueChange={setSelectedRegion}
placeholder={
!selectedCountry
? "Select a country first"
: isLoadingRegions
? "Loading regions..."
: regionOptions.length === 0
? "No regions available"
: "Select region"
}
searchPlaceholder="Search regions..."
disabled={!selectedCountry || isLoadingRegions}
/>
</div>
{selectedState && cityOptions.length > 0 && (
<div className="space-y-2">
<Label>City (optional)</Label>
<Combobox
options={cityOptions}
value={selectedCity}
onValueChange={setSelectedCity}
placeholder={isLoadingCities ? "Loading..." : "Select city"}
searchPlaceholder="Search cities..."
/>
</div>
)}
{/* City - always visible, disabled until country is selected */}
<div className="space-y-2">
<Label className="flex items-center gap-2">
City (optional)
{isLoadingCities && <LoadingSpinner />}
</Label>
<Combobox
options={cityOptions}
value={selectedCity}
onValueChange={setSelectedCity}
placeholder={
!selectedCountry
? "Select a country first"
: isLoadingCities
? "Loading cities..."
: cityOptions.length === 0
? "No cities available"
: "Select city"
}
searchPlaceholder="Search cities..."
disabled={!selectedCountry || isLoadingCities}
/>
</div>
{/* ISP - always visible, disabled until country is selected */}
<div className="space-y-2">
<Label className="flex items-center gap-2">
ISP (optional)
{isLoadingIsps && <LoadingSpinner />}
</Label>
<Combobox
options={ispOptions}
value={selectedIsp}
onValueChange={setSelectedIsp}
placeholder={
!selectedCountry
? "Select a country first"
: isLoadingIsps
? "Loading ISPs..."
: ispOptions.length === 0
? "No ISPs available"
: "Select ISP"
}
searchPlaceholder="Search ISPs..."
disabled={!selectedCountry || isLoadingIsps}
/>
</div>
{/* Name */}
<div className="space-y-2">
<Label>Name</Label>
<Input