refactor: block selection if the profile is launching or stopping

This commit is contained in:
zhom
2025-08-04 07:02:38 +04:00
parent e675441171
commit 83f4c2c162
3 changed files with 150 additions and 18 deletions
+58 -10
View File
@@ -106,6 +106,9 @@ export function ProfilesDataTable({
const [launchingProfiles, setLaunchingProfiles] = React.useState<Set<string>>( const [launchingProfiles, setLaunchingProfiles] = React.useState<Set<string>>(
new Set(), new Set(),
); );
const [stoppingProfiles, setStoppingProfiles] = React.useState<Set<string>>(
new Set(),
);
const [storedProxies, setStoredProxies] = React.useState<StoredProxy[]>([]); const [storedProxies, setStoredProxies] = React.useState<StoredProxy[]>([]);
const [selectedProfiles, setSelectedProfiles] = React.useState<Set<string>>( const [selectedProfiles, setSelectedProfiles] = React.useState<Set<string>>(
@@ -156,6 +159,8 @@ export function ProfilesDataTable({
filteredData, filteredData,
runningProfiles, runningProfiles,
isUpdating, isUpdating,
launchingProfiles,
stoppingProfiles,
); );
// Load stored proxies // Load stored proxies
@@ -174,7 +179,7 @@ export function ProfilesDataTable({
} }
}, [browserState.isClient, loadStoredProxies]); }, [browserState.isClient, loadStoredProxies]);
// Automatically deselect profiles that become running or updating // Automatically deselect profiles that become running, updating, launching, or stopping
React.useEffect(() => { React.useEffect(() => {
setSelectedProfiles((prev) => { setSelectedProfiles((prev) => {
const newSet = new Set(prev); const newSet = new Set(prev);
@@ -185,9 +190,11 @@ export function ProfilesDataTable({
if (profile) { if (profile) {
const isRunning = const isRunning =
browserState.isClient && runningProfiles.has(profile.name); browserState.isClient && runningProfiles.has(profile.name);
const isLaunching = launchingProfiles.has(profile.name);
const isStopping = stoppingProfiles.has(profile.name);
const isBrowserUpdating = isUpdating(profile.browser); const isBrowserUpdating = isUpdating(profile.browser);
if (isRunning || isBrowserUpdating) { if (isRunning || isLaunching || isStopping || isBrowserUpdating) {
newSet.delete(profileName); newSet.delete(profileName);
hasChanges = true; hasChanges = true;
} }
@@ -204,6 +211,8 @@ export function ProfilesDataTable({
}, [ }, [
filteredData, filteredData,
runningProfiles, runningProfiles,
launchingProfiles,
stoppingProfiles,
isUpdating, isUpdating,
browserState.isClient, browserState.isClient,
onSelectedProfilesChange, onSelectedProfilesChange,
@@ -336,8 +345,15 @@ export function ProfilesDataTable({
.filter((profile) => { .filter((profile) => {
const isRunning = const isRunning =
browserState.isClient && runningProfiles.has(profile.name); browserState.isClient && runningProfiles.has(profile.name);
const isLaunching = launchingProfiles.has(profile.name);
const isStopping = stoppingProfiles.has(profile.name);
const isBrowserUpdating = isUpdating(profile.browser); const isBrowserUpdating = isUpdating(profile.browser);
return !isRunning && !isBrowserUpdating; return (
!isRunning &&
!isLaunching &&
!isStopping &&
!isBrowserUpdating
);
}) })
.map((profile) => profile.name), .map((profile) => profile.name),
) )
@@ -356,6 +372,8 @@ export function ProfilesDataTable({
onSelectedProfilesChange, onSelectedProfilesChange,
browserState.isClient, browserState.isClient,
runningProfiles, runningProfiles,
launchingProfiles,
stoppingProfiles,
isUpdating, isUpdating,
], ],
); );
@@ -368,8 +386,12 @@ export function ProfilesDataTable({
const selectableProfiles = filteredData.filter((profile) => { const selectableProfiles = filteredData.filter((profile) => {
const isRunning = const isRunning =
browserState.isClient && runningProfiles.has(profile.name); browserState.isClient && runningProfiles.has(profile.name);
const isLaunching = launchingProfiles.has(profile.name);
const isStopping = stoppingProfiles.has(profile.name);
const isBrowserUpdating = isUpdating(profile.browser); const isBrowserUpdating = isUpdating(profile.browser);
return !isRunning && !isBrowserUpdating; return (
!isRunning && !isLaunching && !isStopping && !isBrowserUpdating
);
}); });
return ( return (
@@ -393,14 +415,21 @@ export function ProfilesDataTable({
const isSelected = selectedProfiles.has(profile.name); const isSelected = selectedProfiles.has(profile.name);
const isRunning = const isRunning =
browserState.isClient && runningProfiles.has(profile.name); browserState.isClient && runningProfiles.has(profile.name);
const isLaunching = launchingProfiles.has(profile.name);
const isStopping = stoppingProfiles.has(profile.name);
const isBrowserUpdating = isUpdating(browser); const isBrowserUpdating = isUpdating(browser);
const isDisabled = isRunning || isBrowserUpdating; const isDisabled =
isRunning || isLaunching || isStopping || isBrowserUpdating;
// Show tooltip for disabled profiles // Show tooltip for disabled profiles
if (isDisabled) { if (isDisabled) {
const tooltipMessage = isRunning const tooltipMessage = isRunning
? "Can't modify running profile" ? "Can't modify running profile"
: "Can't modify profile while browser is updating"; : isLaunching
? "Can't modify profile while launching"
: isStopping
? "Can't modify profile while stopping"
: "Can't modify profile while browser is updating";
return ( return (
<Tooltip> <Tooltip>
@@ -462,6 +491,7 @@ export function ProfilesDataTable({
const isRunning = const isRunning =
browserState.isClient && runningProfiles.has(profile.name); browserState.isClient && runningProfiles.has(profile.name);
const isLaunching = launchingProfiles.has(profile.name); const isLaunching = launchingProfiles.has(profile.name);
const isStopping = stoppingProfiles.has(profile.name);
const canLaunch = browserState.canLaunchProfile(profile); const canLaunch = browserState.canLaunchProfile(profile);
const tooltipContent = browserState.getLaunchTooltipContent(profile); const tooltipContent = browserState.getLaunchTooltipContent(profile);
@@ -470,7 +500,24 @@ export function ProfilesDataTable({
console.log( console.log(
`Stopping ${profile.browser} profile: ${profile.name}`, `Stopping ${profile.browser} profile: ${profile.name}`,
); );
await onKillProfile(profile); setStoppingProfiles((prev) => new Set(prev).add(profile.name));
try {
await onKillProfile(profile);
console.log(
`Successfully stopped ${profile.browser} profile: ${profile.name}`,
);
} catch (error) {
console.error(
`Failed to stop ${profile.browser} profile: ${profile.name}`,
error,
);
} finally {
setStoppingProfiles((prev) => {
const next = new Set(prev);
next.delete(profile.name);
return next;
});
}
} else { } else {
console.log( console.log(
`Launching ${profile.browser} profile: ${profile.name}`, `Launching ${profile.browser} profile: ${profile.name}`,
@@ -504,14 +551,14 @@ export function ProfilesDataTable({
<Button <Button
variant={isRunning ? "destructive" : "default"} variant={isRunning ? "destructive" : "default"}
size="sm" size="sm"
disabled={!canLaunch || isLaunching} disabled={!canLaunch || isLaunching || isStopping}
className={cn( className={cn(
"cursor-pointer min-w-[70px]", "cursor-pointer min-w-[70px]",
!canLaunch && "opacity-50", !canLaunch && "opacity-50",
)} )}
onClick={() => void handleLaunchClick()} onClick={() => void handleLaunchClick()}
> >
{isLaunching ? ( {isLaunching || isStopping ? (
<div className="flex gap-1 items-center"> <div className="flex gap-1 items-center">
<div className="w-3 h-3 rounded-full border border-current animate-spin border-t-transparent" /> <div className="w-3 h-3 rounded-full border border-current animate-spin border-t-transparent" />
</div> </div>
@@ -808,7 +855,8 @@ export function ProfilesDataTable({
onChangeVersion, onChangeVersion,
onAssignProfilesToGroup, onAssignProfilesToGroup,
isUpdating, isUpdating,
launchingProfiles.has, launchingProfiles,
stoppingProfiles,
filteredData, filteredData,
browserState.isClient, browserState.isClient,
], ],
+31 -1
View File
@@ -51,9 +51,21 @@ export function ProfileSelectorDialog({
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const [isLaunching, setIsLaunching] = useState(false); const [isLaunching, setIsLaunching] = useState(false);
const [storedProxies, setStoredProxies] = useState<StoredProxy[]>([]); const [storedProxies, setStoredProxies] = useState<StoredProxy[]>([]);
const [launchingProfiles, setLaunchingProfiles] = useState<Set<string>>(
new Set(),
);
const [stoppingProfiles, _setStoppingProfiles] = useState<Set<string>>(
new Set(),
);
// Use shared browser state hook // Use shared browser state hook
const browserState = useBrowserState(profiles, runningProfiles, isUpdating); const browserState = useBrowserState(
profiles,
runningProfiles,
isUpdating,
launchingProfiles,
stoppingProfiles,
);
// Helper function to check if a profile has a proxy // Helper function to check if a profile has a proxy
const hasProxy = useCallback( const hasProxy = useCallback(
@@ -116,6 +128,7 @@ export function ProfileSelectorDialog({
if (!selectedProfile || !url) return; if (!selectedProfile || !url) return;
setIsLaunching(true); setIsLaunching(true);
setLaunchingProfiles((prev) => new Set(prev).add(selectedProfile));
try { try {
await invoke("open_url_with_profile", { await invoke("open_url_with_profile", {
profileName: selectedProfile, profileName: selectedProfile,
@@ -126,6 +139,11 @@ export function ProfileSelectorDialog({
console.error("Failed to open URL with profile:", error); console.error("Failed to open URL with profile:", error);
} finally { } finally {
setIsLaunching(false); setIsLaunching(false);
setLaunchingProfiles((prev) => {
const next = new Set(prev);
next.delete(selectedProfile);
return next;
});
} }
}, [selectedProfile, url, onClose]); }, [selectedProfile, url, onClose]);
@@ -221,6 +239,8 @@ export function ProfileSelectorDialog({
<SelectContent> <SelectContent>
{profiles.map((profile) => { {profiles.map((profile) => {
const isRunning = runningProfiles.has(profile.name); const isRunning = runningProfiles.has(profile.name);
const isLaunching = launchingProfiles.has(profile.name);
const isStopping = stoppingProfiles.has(profile.name);
const canUseForLinks = const canUseForLinks =
browserState.canUseProfileForLinks(profile); browserState.canUseProfileForLinks(profile);
const tooltipContent = getProfileTooltipContent(profile); const tooltipContent = getProfileTooltipContent(profile);
@@ -269,6 +289,16 @@ export function ProfileSelectorDialog({
Running Running
</Badge> </Badge>
)} )}
{isLaunching && (
<Badge variant="outline" className="text-xs">
Launching
</Badge>
)}
{isStopping && (
<Badge variant="outline" className="text-xs">
Stopping
</Badge>
)}
{!canUseForLinks && ( {!canUseForLinks && (
<Badge <Badge
variant="destructive" variant="destructive"
+61 -7
View File
@@ -8,7 +8,9 @@ import type { BrowserProfile } from "@/types";
export function useBrowserState( export function useBrowserState(
profiles: BrowserProfile[], profiles: BrowserProfile[],
runningProfiles: Set<string>, runningProfiles: Set<string>,
isUpdating?: (browser: string) => boolean, isUpdating: (browser: string) => boolean,
launchingProfiles: Set<string>,
stoppingProfiles: Set<string>,
) { ) {
const [isClient, setIsClient] = useState(false); const [isClient, setIsClient] = useState(false);
@@ -47,8 +49,15 @@ export function useBrowserState(
if (!isClient) return false; if (!isClient) return false;
const isRunning = runningProfiles.has(profile.name); const isRunning = runningProfiles.has(profile.name);
const isLaunching = launchingProfiles?.has(profile.name) ?? false;
const isStopping = stoppingProfiles?.has(profile.name) ?? false;
const isBrowserUpdating = isUpdating?.(profile.browser) ?? false; const isBrowserUpdating = isUpdating?.(profile.browser) ?? false;
// If the profile is launching or stopping, disable the button
if (isLaunching || isStopping) {
return false;
}
// If the profile is already running, it can always be stopped // If the profile is already running, it can always be stopped
if (isRunning) return true; if (isRunning) return true;
@@ -70,6 +79,8 @@ export function useBrowserState(
isUpdating, isUpdating,
isSingleInstanceBrowser, isSingleInstanceBrowser,
isAnyInstanceRunning, isAnyInstanceRunning,
launchingProfiles,
stoppingProfiles,
], ],
); );
@@ -82,10 +93,12 @@ export function useBrowserState(
if (!isClient) return false; if (!isClient) return false;
const isRunning = runningProfiles.has(profile.name); const isRunning = runningProfiles.has(profile.name);
const isLaunching = launchingProfiles?.has(profile.name) ?? false;
const isStopping = stoppingProfiles?.has(profile.name) ?? false;
const isBrowserUpdating = isUpdating?.(profile.browser) ?? false; const isBrowserUpdating = isUpdating?.(profile.browser) ?? false;
// If this specific browser is updating or downloading, block it // If this specific browser is updating, downloading, launching, or stopping, block it
if (isBrowserUpdating) { if (isBrowserUpdating || isLaunching || isStopping) {
return false; return false;
} }
@@ -107,7 +120,15 @@ export function useBrowserState(
// For other browsers, any profile can be used // For other browsers, any profile can be used
return true; return true;
}, },
[profiles, runningProfiles, isClient, isSingleInstanceBrowser, isUpdating], [
profiles,
runningProfiles,
isClient,
isSingleInstanceBrowser,
isUpdating,
launchingProfiles,
stoppingProfiles,
],
); );
/** /**
@@ -117,16 +138,25 @@ export function useBrowserState(
(profile: BrowserProfile): boolean => { (profile: BrowserProfile): boolean => {
if (!isClient) return false; if (!isClient) return false;
const isRunning = runningProfiles.has(profile.name);
const isLaunching = launchingProfiles?.has(profile.name) ?? false;
const isStopping = stoppingProfiles?.has(profile.name) ?? false;
const isBrowserUpdating = isUpdating?.(profile.browser) ?? false; const isBrowserUpdating = isUpdating?.(profile.browser) ?? false;
// If this specific browser is updating or downloading, block selection // If profile is running, launching, stopping, or browser is updating, block selection
if (isBrowserUpdating) { if (isRunning || isLaunching || isStopping || isBrowserUpdating) {
return false; return false;
} }
return true; return true;
}, },
[isClient, isUpdating], [
isClient,
runningProfiles,
launchingProfiles,
stoppingProfiles,
isUpdating,
],
); );
/** /**
@@ -137,8 +167,18 @@ export function useBrowserState(
if (!isClient) return "Loading..."; if (!isClient) return "Loading...";
const isRunning = runningProfiles.has(profile.name); const isRunning = runningProfiles.has(profile.name);
const isLaunching = launchingProfiles?.has(profile.name) ?? false;
const isStopping = stoppingProfiles?.has(profile.name) ?? false;
const isBrowserUpdating = isUpdating?.(profile.browser) ?? false; const isBrowserUpdating = isUpdating?.(profile.browser) ?? false;
if (isLaunching) {
return "Launching browser...";
}
if (isStopping) {
return "Stopping browser...";
}
if (isRunning) { if (isRunning) {
return ""; return "";
} }
@@ -164,6 +204,8 @@ export function useBrowserState(
isUpdating, isUpdating,
isSingleInstanceBrowser, isSingleInstanceBrowser,
canLaunchProfile, canLaunchProfile,
launchingProfiles,
stoppingProfiles,
], ],
); );
@@ -178,8 +220,18 @@ export function useBrowserState(
if (canUseForLinks) return null; if (canUseForLinks) return null;
const isLaunching = launchingProfiles?.has(profile.name) ?? false;
const isStopping = stoppingProfiles?.has(profile.name) ?? false;
const isBrowserUpdating = isUpdating?.(profile.browser) ?? false; const isBrowserUpdating = isUpdating?.(profile.browser) ?? false;
if (isLaunching) {
return "Profile is currently launching. Please wait.";
}
if (isStopping) {
return "Profile is currently stopping. Please wait.";
}
if (isBrowserUpdating) { if (isBrowserUpdating) {
return `${getBrowserDisplayName(profile.browser)} is being updated. Please wait for the update to complete.`; return `${getBrowserDisplayName(profile.browser)} is being updated. Please wait for the update to complete.`;
} }
@@ -208,6 +260,8 @@ export function useBrowserState(
canUseProfileForLinks, canUseProfileForLinks,
isSingleInstanceBrowser, isSingleInstanceBrowser,
isUpdating, isUpdating,
launchingProfiles,
stoppingProfiles,
], ],
); );