From daa36f008b43102f6ec4653814e858ef1f5cac19 Mon Sep 17 00:00:00 2001
From: zhom <2717306+zhom@users.noreply.github.com>
Date: Sun, 10 Aug 2025 06:57:45 +0400
Subject: [PATCH] refactor: disable mullvad and tor profile creation as well as
nightly releases
---
src-tauri/src/theme_detector.rs | 2 +-
src/app/page.tsx | 25 +++++-
src/components/change-version-dialog.tsx | 101 ++++++++---------------
src/components/create-profile-dialog.tsx | 44 +++++-----
src/components/profile-data-table.tsx | 30 -------
src/components/release-type-selector.tsx | 12 +--
6 files changed, 88 insertions(+), 126 deletions(-)
diff --git a/src-tauri/src/theme_detector.rs b/src-tauri/src/theme_detector.rs
index 7f6055e..0900bc4 100644
--- a/src-tauri/src/theme_detector.rs
+++ b/src-tauri/src/theme_detector.rs
@@ -414,7 +414,7 @@ mod linux {
Err("Could not detect theme from system files".into())
}
- fn is_command_available(command: &str) -> bool {
+ pub fn is_command_available(command: &str) -> bool {
Command::new("which")
.arg(command)
.output()
diff --git a/src/app/page.tsx b/src/app/page.tsx
index ede8b6e..790bb0a 100644
--- a/src/app/page.tsx
+++ b/src/app/page.tsx
@@ -23,7 +23,7 @@ import { useAppUpdateNotifications } from "@/hooks/use-app-update-notifications"
import type { PermissionType } from "@/hooks/use-permissions";
import { usePermissions } from "@/hooks/use-permissions";
import { useUpdateNotifications } from "@/hooks/use-update-notifications";
-import { showErrorToast } from "@/lib/toast-utils";
+import { showErrorToast, showToast } from "@/lib/toast-utils";
import type { BrowserProfile, CamoufoxConfig, GroupWithCount } from "@/types";
type BrowserTypeString =
@@ -713,6 +713,29 @@ export default function Home() {
loadGroups,
]);
+ // Show deprecation warning for unsupported profiles
+ useEffect(() => {
+ if (profiles.length === 0) return;
+
+ const hasDeprecated = profiles.some(
+ (p) =>
+ ["tor-browser", "mullvad-browser"].includes(p.browser) ||
+ (p.release_type === "nightly" && p.browser !== "firefox-developer"),
+ );
+
+ if (hasDeprecated) {
+ // Use a stable id to avoid duplicate toasts on re-renders
+ showToast({
+ id: "deprecated-profiles-warning",
+ type: "error",
+ title: "Some profiles will be deprecated soon",
+ description:
+ "Tor, Mullvad Browser and nightly profiles (except Firefox Developers Edition) will be removed in upcoming versions. Please check GitHub for migration instructions which will be available soon.",
+ duration: 15000,
+ });
+ }
+ }, [profiles]);
+
useEffect(() => {
if (profiles.length === 0) return;
diff --git a/src/components/change-version-dialog.tsx b/src/components/change-version-dialog.tsx
index 5a598b7..bf99923 100644
--- a/src/components/change-version-dialog.tsx
+++ b/src/components/change-version-dialog.tsx
@@ -2,11 +2,9 @@
import { invoke } from "@tauri-apps/api/core";
import { useCallback, useEffect, useState } from "react";
-import { LuTriangleAlert } from "react-icons/lu";
import { LoadingButton } from "@/components/loading-button";
import { ReleaseTypeSelector } from "@/components/release-type-selector";
-import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
-import { Checkbox } from "@/components/ui/checkbox";
+import { Alert, AlertDescription } from "@/components/ui/alert";
import {
Dialog,
DialogContent,
@@ -39,8 +37,8 @@ export function ChangeVersionDialog({
const [releaseTypes, setReleaseTypes] = useState({});
const [isLoadingReleaseTypes, setIsLoadingReleaseTypes] = useState(false);
const [isUpdating, setIsUpdating] = useState(false);
- const [showDowngradeWarning, setShowDowngradeWarning] = useState(false);
- const [acknowledgeDowngrade, setAcknowledgeDowngrade] = useState(false);
+ // Nightly switching is disabled for non-nightly profiles (except Firefox Developer),
+ // so downgrade warnings are no longer applicable.
const {
downloadedVersions,
@@ -50,38 +48,36 @@ export function ChangeVersionDialog({
isVersionDownloaded,
} = useBrowserDownload();
- const loadReleaseTypes = useCallback(async (browser: string) => {
- setIsLoadingReleaseTypes(true);
- try {
- const releaseTypes = await invoke(
- "get_browser_release_types",
- { browserStr: browser },
- );
- setReleaseTypes(releaseTypes);
- } catch (error) {
- console.error("Failed to load release types:", error);
- } finally {
- setIsLoadingReleaseTypes(false);
- }
- }, []);
-
- useEffect(() => {
- if (
- profile &&
- selectedReleaseType &&
- selectedReleaseType !== profile.release_type
- ) {
- // For simplicity, we'll show downgrade warning when switching from stable to nightly
- // since nightly versions might be considered "downgrades" in terms of stability
- const isDowngrade =
- profile.release_type === "stable" && selectedReleaseType === "nightly";
- setShowDowngradeWarning(isDowngrade);
-
- if (!isDowngrade) {
- setAcknowledgeDowngrade(false);
+ const loadReleaseTypes = useCallback(
+ async (browser: string) => {
+ setIsLoadingReleaseTypes(true);
+ try {
+ const releaseTypes = await invoke(
+ "get_browser_release_types",
+ { browserStr: browser },
+ );
+ // Filter nightly visibility based on rules:
+ // - Firefox Developer Edition: allow nightly only
+ // - If profile is currently nightly: allow both stable and nightly
+ // - Otherwise: allow stable only
+ const filtered: BrowserReleaseTypes = {};
+ if (profile?.browser === "firefox-developer") {
+ if (releaseTypes.nightly) filtered.nightly = releaseTypes.nightly;
+ } else if (profile?.release_type === "nightly") {
+ if (releaseTypes.stable) filtered.stable = releaseTypes.stable;
+ if (releaseTypes.nightly) filtered.nightly = releaseTypes.nightly;
+ } else {
+ if (releaseTypes.stable) filtered.stable = releaseTypes.stable;
+ }
+ setReleaseTypes(filtered);
+ } catch (error) {
+ console.error("Failed to load release types:", error);
+ } finally {
+ setIsLoadingReleaseTypes(false);
}
- }
- }, [selectedReleaseType, profile]);
+ },
+ [profile?.browser, profile?.release_type],
+ );
const handleDownload = useCallback(async () => {
if (!profile || !selectedReleaseType) return;
@@ -129,14 +125,12 @@ export function ChangeVersionDialog({
selectedReleaseType &&
selectedReleaseType !== profile.release_type &&
selectedVersion &&
- isVersionDownloaded(selectedVersion) &&
- (!showDowngradeWarning || acknowledgeDowngrade);
+ isVersionDownloaded(selectedVersion);
useEffect(() => {
if (isOpen && profile) {
// Set current release type based on profile
setSelectedReleaseType(profile.release_type as "stable" | "nightly");
- setAcknowledgeDowngrade(false);
void loadReleaseTypes(profile.browser);
void loadDownloadedVersions(profile.browser);
}
@@ -206,7 +200,6 @@ export function ChangeVersionDialog({
selectedReleaseType={selectedReleaseType}
onReleaseTypeSelect={setSelectedReleaseType}
availableReleaseTypes={releaseTypes}
- browser={profile.browser}
isDownloading={isBrowserDownloading(profile.browser)}
onDownload={() => {
void handleDownload();
@@ -246,7 +239,6 @@ export function ChangeVersionDialog({
selectedReleaseType={selectedReleaseType}
onReleaseTypeSelect={setSelectedReleaseType}
availableReleaseTypes={releaseTypes}
- browser={profile.browser}
isDownloading={isBrowserDownloading(profile.browser)}
onDownload={() => {
void handleDownload();
@@ -259,32 +251,7 @@ export function ChangeVersionDialog({
)}
- {/* Downgrade Warning */}
- {showDowngradeWarning && (
-
-
-
- Stability Warning
-
-
- You are about to switch from stable to nightly releases. Nightly
- versions may be less stable and could contain bugs or incomplete
- features.
-
- {
- setAcknowledgeDowngrade(checked as boolean);
- }}
- />
-
-
-
-
- )}
+ {/* Nightly switching disabled in UI; no downgrade warning needed. */}
diff --git a/src/components/create-profile-dialog.tsx b/src/components/create-profile-dialog.tsx
index ace563a..127923a 100644
--- a/src/components/create-profile-dialog.tsx
+++ b/src/components/create-profile-dialog.tsx
@@ -191,10 +191,19 @@ export function CreateProfileDialog({
// Only update state if this browser is still the one we're loading
if (loadingBrowserRef.current === browser) {
+ // Filter to enforce stable-only creation, except Firefox Developer (nightly-only)
if (browser === "camoufox") {
- setCamoufoxReleaseTypes(releaseTypes);
+ const filtered: BrowserReleaseTypes = {};
+ if (releaseTypes.stable) filtered.stable = releaseTypes.stable;
+ setCamoufoxReleaseTypes(filtered);
+ } else if (browser === "firefox-developer") {
+ const filtered: BrowserReleaseTypes = {};
+ if (releaseTypes.nightly) filtered.nightly = releaseTypes.nightly;
+ setAvailableReleaseTypes(filtered);
} else {
- setAvailableReleaseTypes(releaseTypes);
+ const filtered: BrowserReleaseTypes = {};
+ if (releaseTypes.stable) filtered.stable = releaseTypes.stable;
+ setAvailableReleaseTypes(filtered);
}
// Load downloaded versions for this browser
@@ -241,26 +250,20 @@ export function CreateProfileDialog({
}
}, [selectedBrowser, loadReleaseTypes]);
- // Helper function to get the best available version and release type
+ // Helper function to get the best available version respecting rules
const getBestAvailableVersion = useCallback(
(releaseTypes: BrowserReleaseTypes, browserType?: string) => {
- // For Firefox Developer Edition, prefer nightly over stable
+ // Firefox Developer Edition: nightly-only
if (browserType === "firefox-developer" && releaseTypes.nightly) {
return {
version: releaseTypes.nightly,
releaseType: "nightly" as const,
};
}
-
+ // All others: stable-only
if (releaseTypes.stable) {
return { version: releaseTypes.stable, releaseType: "stable" as const };
}
- if (releaseTypes.nightly) {
- return {
- version: releaseTypes.nightly,
- releaseType: "nightly" as const,
- };
- }
return null;
},
[],
@@ -438,8 +441,11 @@ export function CreateProfileDialog({
- supportedBrowsers.includes(browser.value),
+ .filter(
+ (browser) =>
+ supportedBrowsers.includes(browser.value) &&
+ browser.value !== "mullvad-browser" &&
+ browser.value !== "tor-browser",
)
.map((browser) => {
const IconComponent = getBrowserIcon(browser.value);
@@ -473,7 +479,7 @@ export function CreateProfileDialog({
availableReleaseTypes,
selectedBrowser,
);
- return `${bestVersion?.releaseType === "stable" ? "Latest stable" : "Latest nightly"} version (${bestVersion?.version}) needs to be downloaded`;
+ return `Latest version (${bestVersion?.version}) needs to be downloaded`;
})()}
)}
@@ -509,7 +515,7 @@ export function CreateProfileDialog({
availableReleaseTypes,
selectedBrowser,
);
- return `Downloading ${bestVersion?.releaseType === "stable" ? "stable" : "nightly"} version (${bestVersion?.version})...`;
+ return `Downloading version (${bestVersion?.version})...`;
})()}
)}
@@ -534,7 +540,7 @@ export function CreateProfileDialog({
camoufoxReleaseTypes,
"camoufox",
);
- return `Camoufox ${bestVersion?.releaseType} version (${bestVersion?.version}) needs to be downloaded`;
+ return `Camoufox version (${bestVersion?.version}) needs to be downloaded`;
})()}
)}
@@ -570,7 +576,7 @@ export function CreateProfileDialog({
camoufoxReleaseTypes,
"camoufox",
);
- return `Downloading Camoufox ${bestVersion?.releaseType} version (${bestVersion?.version})...`;
+ return `Downloading Camoufox version (${bestVersion?.version})...`;
})()}
)}
diff --git a/src/components/profile-data-table.tsx b/src/components/profile-data-table.tsx
index 24f588c..62069f6 100644
--- a/src/components/profile-data-table.tsx
+++ b/src/components/profile-data-table.tsx
@@ -666,36 +666,6 @@ export function ProfilesDataTable({
);
},
},
- {
- accessorKey: "release_type",
- header: "Release",
- cell: ({ row }) => {
- const releaseType: string = row.getValue("release_type");
- const isNightly = releaseType === "nightly";
- return (
-
-
- {isNightly ? "Nightly" : "Stable"}
-
-
- );
- },
- enableSorting: true,
- sortingFn: (rowA, rowB, columnId) => {
- const releaseA: string = rowA.getValue(columnId);
- const releaseB: string = rowB.getValue(columnId);
- // Sort with "stable" before "nightly"
- if (releaseA === "stable" && releaseB === "nightly") return -1;
- if (releaseA === "nightly" && releaseB === "stable") return 1;
- return 0;
- },
- },
{
id: "proxy",
header: "Proxy",
diff --git a/src/components/release-type-selector.tsx b/src/components/release-type-selector.tsx
index 82202a6..3e811ac 100644
--- a/src/components/release-type-selector.tsx
+++ b/src/components/release-type-selector.tsx
@@ -24,7 +24,6 @@ interface ReleaseTypeSelectorProps {
selectedReleaseType: "stable" | "nightly" | null;
onReleaseTypeSelect: (releaseType: "stable" | "nightly" | null) => void;
availableReleaseTypes: BrowserReleaseTypes;
- browser: string;
isDownloading: boolean;
onDownload: () => void;
placeholder?: string;
@@ -36,7 +35,7 @@ export function ReleaseTypeSelector({
selectedReleaseType,
onReleaseTypeSelect,
availableReleaseTypes,
- browser,
+ // browser prop removed; callers control availableReleaseTypes
isDownloading,
onDownload,
placeholder = "Select release type...",
@@ -45,11 +44,13 @@ export function ReleaseTypeSelector({
}: ReleaseTypeSelectorProps) {
const [popoverOpen, setPopoverOpen] = useState(false);
+ // Nightly visibility is controlled by callers. This component will render
+ // whichever options are provided via availableReleaseTypes.
const releaseOptions = [
...(availableReleaseTypes.stable
? [{ type: "stable" as const, version: availableReleaseTypes.stable }]
: []),
- ...(availableReleaseTypes.nightly && browser !== "chromium"
+ ...(availableReleaseTypes.nightly
? [{ type: "nightly" as const, version: availableReleaseTypes.nightly }]
: []),
];
@@ -159,11 +160,6 @@ export function ReleaseTypeSelector({
{releaseOptions[0].type}
- {releaseOptions[0].type === "nightly" && (
-
- Nightly
-
- )}
{releaseOptions[0].version}