diff --git a/src/app/page.tsx b/src/app/page.tsx index 8070434..f3bcbea 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -704,25 +704,35 @@ export default function Home() { loadGroups, ]); - // Show deprecation warning for unsupported profiles + // Show deprecation warning for unsupported profiles (with names) useEffect(() => { if (profiles.length === 0) return; - const hasDeprecated = profiles.some( + const deprecatedProfiles = profiles.filter( (p) => ["tor-browser", "mullvad-browser"].includes(p.browser) || (p.release_type === "nightly" && p.browser !== "firefox-developer"), ); - if (hasDeprecated) { + if (deprecatedProfiles.length > 0) { + const deprecatedNames = deprecatedProfiles.map((p) => p.name).join(", "); + // 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.", + description: `The following profiles will be deprecated soon: ${deprecatedNames}. Tor Browser, Mullvad Browser, and nightly profiles (except Firefox Developers Edition) will be removed in upcoming versions. Please check GitHub for migration instructions.`, duration: 15000, + action: { + label: "Learn more", + onClick: () => { + const event = new CustomEvent("url-open-request", { + detail: "https://github.com/zhom/donutbrowser/discussions/66", + }); + window.dispatchEvent(event); + }, + }, }); } }, [profiles]); diff --git a/src/components/custom-toast.tsx b/src/components/custom-toast.tsx index cf45972..b3b78ac 100644 --- a/src/components/custom-toast.tsx +++ b/src/components/custom-toast.tsx @@ -55,12 +55,16 @@ import { LuRocket, LuTriangleAlert, } from "react-icons/lu"; +import type { ExternalToast } from "sonner"; +import { Button } from "./ui/button"; +import { RippleButton } from "./ui/ripple"; interface BaseToastProps { id?: string; title: string; description?: string; duration?: number; + action?: ExternalToast["action"]; } interface LoadingToastProps extends BaseToastProps { @@ -158,7 +162,7 @@ function getToastIcon(type: ToastProps["type"], stage?: string) { } export function UnifiedToast(props: ToastProps) { - const { title, description, type } = props; + const { title, description, type, action } = props; const stage = "stage" in props ? props.stage : undefined; const progress = "progress" in props ? props.progress : undefined; @@ -289,6 +293,19 @@ export function UnifiedToast(props: ToastProps) { )} )} + {action && + "onClick" in (action as any) && + "label" in (action as any) && ( +
+ + {(action as any).label} + +
+ )} ); diff --git a/src/lib/toast-utils.ts b/src/lib/toast-utils.ts index 66f5c8a..8d838c5 100644 --- a/src/lib/toast-utils.ts +++ b/src/lib/toast-utils.ts @@ -1,5 +1,5 @@ import React from "react"; -import { toast as sonnerToast } from "sonner"; +import { type ExternalToast, toast as sonnerToast } from "sonner"; import { UnifiedToast } from "@/components/custom-toast"; interface BaseToastProps { @@ -7,6 +7,7 @@ interface BaseToastProps { title: string; description?: string; duration?: number; + action?: ExternalToast["action"]; } interface LoadingToastProps extends BaseToastProps {