chore: linting for both js and rs

This commit is contained in:
zhom
2025-05-29 23:57:54 +04:00
parent cf57b2a043
commit 5c02b59af4
33 changed files with 836 additions and 608 deletions
+1 -1
View File
@@ -16,8 +16,8 @@ import { VersionSelector } from "@/components/version-selector";
import { useBrowserDownload } from "@/hooks/use-browser-download";
import type { BrowserProfile } from "@/types";
import { invoke } from "@tauri-apps/api/core";
import { LuTriangleAlert } from "react-icons/lu";
import { useEffect, useState } from "react";
import { LuTriangleAlert } from "react-icons/lu";
interface ChangeVersionDialogProps {
isOpen: boolean;
+1 -204
View File
@@ -43,12 +43,11 @@
*/
import React from "react";
import { toast as sonnerToast } from "sonner";
import {
LuCheckCheck,
LuTriangleAlert,
LuDownload,
LuRefreshCw,
LuTriangleAlert,
} from "react-icons/lu";
interface BaseToastProps {
@@ -123,7 +122,6 @@ function getToastIcon(type: ToastProps["type"], stage?: string) {
return (
<LuRefreshCw className="h-4 w-4 text-blue-500 animate-spin flex-shrink-0" />
);
case "loading":
default:
return (
<div className="animate-spin rounded-full h-4 w-4 border-2 border-blue-500 border-t-transparent flex-shrink-0" />
@@ -214,204 +212,3 @@ export function UnifiedToast(props: ToastProps) {
</div>
);
}
// Unified toast function
export function showToast(props: ToastProps & { id?: string }) {
const toastId = props.id ?? `toast-${props.type}-${Date.now()}`;
// Improved duration logic - make toasts disappear more quickly
let duration: number;
if (props.duration !== undefined) {
duration = props.duration;
} else {
switch (props.type) {
case "loading":
case "fetching":
duration = 10000; // 10 seconds instead of infinite
break;
case "download":
// Only keep infinite for active downloading, others get shorter durations
if ("stage" in props && props.stage === "downloading") {
duration = Number.POSITIVE_INFINITY;
} else if ("stage" in props && props.stage === "completed") {
duration = 3000; // Shorter duration for completed downloads
} else {
duration = 8000; // 8 seconds for extracting/verifying
}
break;
case "version-update":
duration = 15000; // 15 seconds instead of infinite
break;
case "success":
duration = 3000; // Shorter success duration
break;
case "error":
duration = 5000; // Reasonable error duration
break;
default:
duration = 4000;
}
}
if (props.type === "success") {
sonnerToast.success(<UnifiedToast {...props} />, {
id: toastId,
duration,
style: {
background: "transparent",
border: "none",
boxShadow: "none",
padding: 0,
},
});
} else if (props.type === "error") {
sonnerToast.error(<UnifiedToast {...props} />, {
id: toastId,
duration,
style: {
background: "transparent",
border: "none",
boxShadow: "none",
padding: 0,
},
});
} else {
sonnerToast.custom((id) => <UnifiedToast {...props} />, {
id: toastId,
duration,
style: {
background: "transparent",
border: "none",
boxShadow: "none",
padding: 0,
},
});
}
return toastId;
}
// Convenience functions for common use cases
export function showLoadingToast(
title: string,
options?: {
id?: string;
description?: string;
duration?: number;
}
) {
return showToast({
type: "loading",
title,
...options,
});
}
export function showDownloadToast(
browserName: string,
version: string,
stage: "downloading" | "extracting" | "verifying" | "completed",
progress?: { percentage: number; speed?: string; eta?: string },
options?: { suppressCompletionToast?: boolean }
) {
const title =
stage === "completed"
? `${browserName} ${version} downloaded successfully!`
: stage === "downloading"
? `Downloading ${browserName} ${version}`
: stage === "extracting"
? `Extracting ${browserName} ${version}`
: `Verifying ${browserName} ${version}`;
// Don't show completion toast if suppressed (for auto-update scenarios)
if (stage === "completed" && options?.suppressCompletionToast) {
dismissToast(`download-${browserName.toLowerCase()}-${version}`);
return;
}
return showToast({
type: "download",
title,
stage,
progress,
id: `download-${browserName.toLowerCase()}-${version}`,
});
}
export function showVersionUpdateToast(
title: string,
options?: {
id?: string;
description?: string;
progress?: {
current: number;
total: number;
found: number;
};
duration?: number;
}
) {
return showToast({
type: "version-update",
title,
...options,
});
}
export function showFetchingToast(
browserName: string,
options?: {
id?: string;
description?: string;
duration?: number;
}
) {
return showToast({
type: "fetching",
title: `Checking for new ${browserName} versions...`,
description:
options?.description ?? "Fetching latest release information...",
browserName,
...options,
});
}
export function showSuccessToast(
title: string,
options?: {
id?: string;
description?: string;
duration?: number;
}
) {
return showToast({
type: "success",
title,
...options,
});
}
export function showErrorToast(
title: string,
options?: {
id?: string;
description?: string;
duration?: number;
}
) {
return showToast({
type: "error",
title,
...options,
});
}
// Generic helper for dismissing toasts
export function dismissToast(id: string) {
sonnerToast.dismiss(id);
}
// Dismiss all toasts
export function dismissAllToasts() {
sonnerToast.dismiss();
}
+15 -5
View File
@@ -31,6 +31,8 @@ import {
TooltipContent,
TooltipTrigger,
} from "@/components/ui/tooltip";
import { useTableSorting } from "@/hooks/use-table-sorting";
import { getBrowserDisplayName, getBrowserIcon } from "@/lib/browser-utils";
import type { BrowserProfile } from "@/types";
import {
type ColumnDef,
@@ -40,14 +42,12 @@ import {
getSortedRowModel,
useReactTable,
} from "@tanstack/react-table";
import { LuChevronDown, LuChevronUp } from "react-icons/lu";
import { IoEllipsisHorizontal } from "react-icons/io5";
import * as React from "react";
import { CiCircleCheck } from "react-icons/ci";
import { useTableSorting } from "@/hooks/use-table-sorting";
import { IoEllipsisHorizontal } from "react-icons/io5";
import { LuChevronDown, LuChevronUp } from "react-icons/lu";
import { Input } from "./ui/input";
import { Label } from "./ui/label";
import { getBrowserDisplayName, getBrowserIcon } from "@/lib/browser-utils";
interface ProfilesDataTableProps {
data: BrowserProfile[];
@@ -392,7 +392,17 @@ export function ProfilesDataTable({
},
},
],
[isClient, runningProfiles, isUpdating, data]
[
isClient,
runningProfiles,
isUpdating,
data,
onLaunchProfile,
onKillProfile,
onProxySettings,
onDeleteProfile,
onChangeVersion,
]
);
const table = useReactTable({
+2 -2
View File
@@ -23,12 +23,12 @@ import {
TooltipContent,
TooltipTrigger,
} from "@/components/ui/tooltip";
import { getBrowserDisplayName, getBrowserIcon } from "@/lib/browser-utils";
import type { BrowserProfile } from "@/types";
import { invoke } from "@tauri-apps/api/core";
import { LuCopy } from "react-icons/lu";
import { useEffect, useState } from "react";
import { LuCopy } from "react-icons/lu";
import { toast } from "sonner";
import { getBrowserDisplayName, getBrowserIcon } from "@/lib/browser-utils";
interface ProfileSelectorDialogProps {
isOpen: boolean;
+1 -1
View File
@@ -1,8 +1,8 @@
"use client";
import * as CheckboxPrimitive from "@radix-ui/react-checkbox";
import { LuCheck } from "react-icons/lu";
import type * as React from "react";
import { LuCheck } from "react-icons/lu";
import { cn } from "@/lib/utils";
+1 -1
View File
@@ -1,8 +1,8 @@
"use client";
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
import { LuCheck, LuChevronRight, LuCircle } from "react-icons/lu";
import type * as React from "react";
import { LuCheck, LuChevronRight, LuCircle } from "react-icons/lu";
import { cn } from "@/lib/utils";
+1 -1
View File
@@ -1,8 +1,8 @@
"use client";
import * as SelectPrimitive from "@radix-ui/react-select";
import { LuCheck, LuChevronDown, LuChevronUp } from "react-icons/lu";
import type * as React from "react";
import { LuCheck, LuChevronDown, LuChevronUp } from "react-icons/lu";
import { cn } from "@/lib/utils";
+5 -202
View File
@@ -2,14 +2,12 @@
/* eslint-disable @typescript-eslint/no-misused-promises */
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
import { invoke } from "@tauri-apps/api/core";
import { toast } from "sonner";
import { useCallback, useEffect, useState } from "react";
import { FaDownload, FaTimes } from "react-icons/fa";
import { showToast } from "@/components/custom-toast";
import { Button } from "@/components/ui/button";
import { getBrowserDisplayName } from "@/lib/browser-utils";
import React from "react";
import { FaDownload, FaTimes } from "react-icons/fa";
import { LuDownload } from "react-icons/lu";
interface UpdateNotification {
id: string;
@@ -28,7 +26,7 @@ interface UpdateNotificationProps {
isUpdating?: boolean;
}
function UpdateNotificationComponent({
export function UpdateNotificationComponent({
notification,
onUpdate,
onDismiss,
@@ -108,198 +106,3 @@ function UpdateNotificationComponent({
</div>
);
}
export function useUpdateNotifications() {
const [notifications, setNotifications] = useState<UpdateNotification[]>([]);
const [updatingBrowsers, setUpdatingBrowsers] = useState<Set<string>>(
new Set()
);
const [isClient, setIsClient] = useState(false);
// Ensure we're on the client side to prevent hydration mismatches
useEffect(() => {
setIsClient(true);
}, []);
const checkForUpdates = useCallback(async () => {
if (!isClient) return; // Only run on client side
try {
const updates = await invoke<UpdateNotification[]>(
"check_for_browser_updates"
);
setNotifications(updates);
// Show toasts for new notifications - we'll define handleUpdate and handleDismiss separately
// to avoid circular dependencies
} catch (error) {
console.error("Failed to check for updates:", error);
}
}, [isClient]);
const handleUpdate = useCallback(
async (browser: string, newVersion: string) => {
try {
setUpdatingBrowsers((prev) => new Set(prev).add(browser));
const browserDisplayName = getBrowserDisplayName(browser);
// Dismiss all notifications for this browser first
const browserNotifications = notifications.filter(
(n) => n.browser === browser
);
for (const notification of browserNotifications) {
toast.dismiss(notification.id);
await invoke("dismiss_update_notification", {
notificationId: notification.id,
});
}
try {
// Check if browser already exists before downloading
const isDownloaded = await invoke<boolean>("check_browser_exists", {
browserStr: browser,
version: newVersion,
});
if (isDownloaded) {
// Browser already exists, skip download and go straight to profile update
console.log(
`${browserDisplayName} ${newVersion} already exists, skipping download`
);
} else {
// Mark download as auto-update in the backend for toast suppression
await invoke("mark_auto_update_download", {
browser,
version: newVersion,
});
// Download the browser (progress will be handled by use-browser-download hook)
await invoke("download_browser", {
browserStr: browser,
version: newVersion,
});
}
// Complete the update with auto-update of profile versions
const updatedProfiles = await invoke<string[]>(
"complete_browser_update_with_auto_update",
{
browser,
newVersion,
}
);
// Show success message based on whether profiles were updated
if (updatedProfiles.length > 0) {
const profileText =
updatedProfiles.length === 1
? `Profile "${updatedProfiles[0]}" has been updated`
: `${updatedProfiles.length} profiles have been updated`;
showToast({
type: "success",
title: `${browserDisplayName} update completed`,
description: `${profileText} to version ${newVersion}. Running profiles were not updated and can be updated manually.`,
duration: 5000,
});
} else {
showToast({
type: "success",
title: `${browserDisplayName} update ready`,
description:
"All affected profiles are currently running. Stop them and manually update their versions to use the new version.",
duration: 5000,
});
}
} catch (downloadError) {
console.error("Failed to download browser:", downloadError);
// Clean up auto-update tracking on error
try {
await invoke("remove_auto_update_download", {
browser,
version: newVersion,
});
} catch (e) {
console.error("Failed to clean up auto-update tracking:", e);
}
showToast({
type: "error",
title: `Failed to download ${browserDisplayName} ${newVersion}`,
description: String(downloadError),
duration: 6000,
});
throw downloadError;
}
// Refresh notifications to clear any remaining ones
await checkForUpdates();
} catch (error) {
console.error("Failed to start update:", error);
const browserDisplayName = getBrowserDisplayName(browser);
showToast({
type: "error",
title: `Failed to update ${browserDisplayName}`,
description: String(error),
duration: 6000,
});
} finally {
setUpdatingBrowsers((prev) => {
const next = new Set(prev);
next.delete(browser);
return next;
});
}
},
[notifications, checkForUpdates]
);
const handleDismiss = useCallback(
async (notificationId: string) => {
if (!isClient) return; // Only run on client side
try {
toast.dismiss(notificationId);
await invoke("dismiss_update_notification", { notificationId });
await checkForUpdates();
} catch (error) {
console.error("Failed to dismiss notification:", error);
}
},
[checkForUpdates, isClient]
);
// Separate effect to show toasts when notifications change
useEffect(() => {
if (!isClient) return;
notifications.forEach((notification) => {
const isUpdating = updatingBrowsers.has(notification.browser);
toast.custom(
() => (
<UpdateNotificationComponent
notification={notification}
onUpdate={handleUpdate}
onDismiss={handleDismiss}
isUpdating={isUpdating}
/>
),
{
id: notification.id,
duration: Number.POSITIVE_INFINITY, // Persistent until user action
position: "top-right",
// Remove transparent styling to fix background issue
style: undefined,
}
);
});
}, [notifications, updatingBrowsers, handleUpdate, handleDismiss, isClient]);
return {
notifications,
checkForUpdates,
isUpdating: (browser: string) => updatingBrowsers.has(browser),
};
}
+1 -1
View File
@@ -17,8 +17,8 @@ import {
PopoverTrigger,
} from "@/components/ui/popover";
import { cn } from "@/lib/utils";
import { LuDownload } from "react-icons/lu";
import { useState } from "react";
import { LuDownload } from "react-icons/lu";
import { LuCheck, LuChevronsUpDown } from "react-icons/lu";
import { ScrollArea } from "./ui/scroll-area";
+2 -2
View File
@@ -11,10 +11,10 @@ import {
} from "@/components/ui/card";
import { useVersionUpdater } from "@/hooks/use-version-updater";
import {
LuRefreshCw,
LuClock,
LuCheckCheck,
LuCircleAlert,
LuClock,
LuRefreshCw,
} from "react-icons/lu";
export function VersionUpdateSettings() {