From 5908579c2f55f59c6dc307b76ed97813d4cc5b8b Mon Sep 17 00:00:00 2001 From: Subhamoy Biswas Date: Mon, 11 Aug 2025 12:38:44 +0530 Subject: [PATCH] refactor: switched to sonner from shadcn toast --- src/App.tsx | 18 +++----- src/components/ui/sonner.tsx | 6 +++ src/helpers/use-settings.ts | 18 +++----- src/pages/downloader.tsx | 27 ++++-------- src/pages/library.tsx | 82 +++++++++++++----------------------- src/pages/settings.tsx | 64 +++++++++------------------- 6 files changed, 76 insertions(+), 139 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 383d2ec..52ddf69 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,6 +1,5 @@ import { ThemeProvider } from "@/providers/themeProvider"; import { TooltipProvider } from "@/components/ui/tooltip"; -import { Toaster } from "@/components/ui/toaster"; import { AppContext } from "@/providers/appContextProvider"; import { DownloadState } from "@/types/download"; import { invoke } from "@tauri-apps/api/core"; @@ -25,11 +24,10 @@ import { useNavigate } from "react-router-dom"; import { platform } from "@tauri-apps/plugin-os"; import { useMacOsRegisterer } from "@/helpers/use-macos-registerer"; import useAppUpdater from "@/helpers/use-app-updater"; -import { useToast } from "@/hooks/use-toast"; +import { Toaster as Sonner } from "@/components/ui/sonner"; +import { toast } from "sonner"; export default function App({ children }: { children: React.ReactNode }) { - const { toast } = useToast(); - const { data: downloadStates, isSuccess: isSuccessFetchingDownloadStates } = useFetchAllDownloadStates(); const { data: settings, isSuccess: isSuccessFetchingSettings } = useFetchAllSettings(); const { data: kvPairs, isSuccess: isSuccessFetchingKvPairs } = useFetchAllkVPairs(); @@ -180,10 +178,8 @@ export default function App({ children }: { children: React.ReactNode }) { let videoMetadata = await fetchVideoMetadata(url, selectedFormat, isPlaylist && playlistIndex && typeof playlistIndex === 'string' ? playlistIndex : undefined); if (!videoMetadata) { console.error('Failed to fetch video metadata'); - toast({ - title: 'Download Failed', - description: 'yt-dlp failed to fetch video metadata. Please try again later.', - variant: 'destructive', + toast.error("Download Failed", { + description: "yt-dlp failed to fetch video metadata. Please try again later.", }); return; } @@ -865,10 +861,8 @@ export default function App({ children }: { children: React.ReactNode }) { // show a toast and pause the download when yt-dlp exits unexpectedly useEffect(() => { if (isErrored && !isErrorExpected) { - toast({ - title: "Download Failed", + toast.error("Download Failed", { description: "yt-dlp exited unexpectedly. Please try again later", - variant: "destructive", }); if (erroredDownloadId) { downloadStatusUpdater.mutate({ download_id: erroredDownloadId, download_status: 'paused' }, { @@ -904,7 +898,7 @@ export default function App({ children }: { children: React.ReactNode }) { {children} - + diff --git a/src/components/ui/sonner.tsx b/src/components/ui/sonner.tsx index cd62aff..fe3ca59 100644 --- a/src/components/ui/sonner.tsx +++ b/src/components/ui/sonner.tsx @@ -15,6 +15,12 @@ const Toaster = ({ ...props }: ToasterProps) => { "--normal-border": "var(--border)", } as React.CSSProperties } + toastOptions={{ + classNames: { + toast: "group", + icon: "group-data-[type=error]:!text-red-500 group-data-[type=success]:!text-green-500 group-data-[type=warning]:!text-amber-500 group-data-[type=info]:!text-sky-500", + }, + }} {...props} /> ) diff --git a/src/helpers/use-settings.ts b/src/helpers/use-settings.ts index 1faf9ff..8bd9eb1 100644 --- a/src/helpers/use-settings.ts +++ b/src/helpers/use-settings.ts @@ -1,11 +1,10 @@ -import { useToast } from "@/hooks/use-toast"; +import { toast } from "sonner"; import { useResetSettings, useSaveSettingsKey } from "@/services/mutations"; import { useSettingsPageStatesStore } from "@/services/store"; import { useQueryClient } from "@tanstack/react-query"; import { invoke } from "@tauri-apps/api/core"; export function useSettings() { - const { toast } = useToast(); const queryClient = useQueryClient(); const setSettingsKey = useSettingsPageStatesStore(state => state.setSettingsKey); const resetSettingsState = useSettingsPageStatesStore(state => state.resetSettings); @@ -22,10 +21,8 @@ export function useSettings() { onError: (error) => { console.error("Error saving settings key:", error); queryClient.invalidateQueries({ queryKey: ["settings"] }); - toast({ - title: "Failed to update settings", + toast.error("Failed to update settings", { description: `Failed to update ${key}`, - variant: "destructive", }); } }); @@ -39,26 +36,21 @@ export function useSettings() { resetSettingsState(); console.log("Settings reset successfully"); queryClient.invalidateQueries({ queryKey: ["settings"] }); - toast({ - title: "Settings reset successfully", + toast.success("Settings reset successfully", { description: "All settings have been reset to default.", }); } catch (error) { console.error("Error resetting settings:", error); - toast({ - title: "Failed to reset settings", + toast.error("Failed to reset settings", { description: "Failed to reset settings to default.", - variant: "destructive", }); return; } }, onError: (error) => { console.error("Error resetting settings:", error); - toast({ - title: "Failed to reset settings", + toast.error("Failed to reset settings", { description: "Failed to reset settings to default.", - variant: "destructive", }); } }); diff --git a/src/pages/downloader.tsx b/src/pages/downloader.tsx index 401e0ff..1b7563f 100644 --- a/src/pages/downloader.tsx +++ b/src/pages/downloader.tsx @@ -5,7 +5,7 @@ import { Button } from "@/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; import { Separator } from "@/components/ui/separator"; -import { useToast } from "@/hooks/use-toast"; +import { toast } from "sonner"; import { useAppContext } from "@/providers/appContextProvider"; import { useCurrentVideoMetadataStore, useDownloaderPageStatesStore, useSettingsPageStatesStore } from "@/services/store"; import { determineFileType, fileFormatFilter, formatBitrate, formatDurationString, formatFileSize, formatReleaseDate, formatYtStyleCount, isObjEmpty, sortByBitrate } from "@/utils"; @@ -35,7 +35,6 @@ const searchFormSchema = z.object({ export default function DownloaderPage() { const { fetchVideoMetadata, startDownload } = useAppContext(); - const { toast } = useToast(); const videoUrl = useCurrentVideoMetadataStore((state) => state.videoUrl); const videoMetadata = useCurrentVideoMetadataStore((state) => state.videoMetadata); @@ -226,10 +225,8 @@ export default function DownloaderPage() { if (!metadata || (metadata._type !== 'video' && metadata._type !== 'playlist') || (metadata && metadata._type === 'video' && metadata.formats.length <= 0) || (metadata && metadata._type === 'playlist' && metadata.entries.length <= 0)) { const showSearchError = useCurrentVideoMetadataStore.getState().showSearchError; if (showSearchError) { - toast({ - title: 'Oops! No results found', - description: 'The provided URL does not contain any downloadable content or you are not connected to the internet. Please check the URL, your network connection and try again.', - variant: "destructive" + toast.error("Oops! No results found", { + description: "The provided URL does not contain any downloadable content or you are not connected to the internet. Please check the URL, your network connection and try again.", }); } } @@ -307,20 +304,16 @@ export default function DownloaderPage() { // If URL is invalid, just reset the flag setAutoSubmitSearch(false); setRequestedUrl(''); - toast({ - title: 'Invalid URL', - description: 'The provided URL is not valid.', - variant: "destructive" + toast.error("Invalid URL", { + description: "The provided URL is not valid.", }); } } else { // If metadata is loading, just reset the flag setAutoSubmitSearch(false); setRequestedUrl(''); - toast({ - title: 'Search in progress', - description: 'Search in progress, try again later.', - variant: "destructive" + toast.info("Search in progress", { + description: "There's a search in progress, Please try again later.", }); } } else { @@ -941,10 +934,8 @@ export default function DownloaderPage() { // }); } catch (error) { console.error('Download failed to start:', error); - toast({ - title: 'Failed to Start Download', - description: 'There was an error initiating the download.', - variant: "destructive" + toast.error("Failed to Start Download", { + description: "There was an error initiating the download." }); } finally { setIsStartingDownload(false); diff --git a/src/pages/library.tsx b/src/pages/library.tsx index 962ba48..9e01768 100644 --- a/src/pages/library.tsx +++ b/src/pages/library.tsx @@ -4,7 +4,7 @@ import { AspectRatio } from "@/components/ui/aspect-ratio"; import { Button } from "@/components/ui/button"; import { Progress } from "@/components/ui/progress"; import { Separator } from "@/components/ui/separator"; -import { useToast } from "@/hooks/use-toast"; +import { toast } from "sonner"; import { useAppContext } from "@/providers/appContextProvider"; import { useDownloadActionStatesStore, useDownloadStatesStore, useLibraryPageStatesStore } from "@/services/store"; import { formatBitrate, formatCodec, formatDurationString, formatFileSize, formatSecToTimeString, formatSpeed } from "@/utils"; @@ -21,7 +21,6 @@ import Heading from "@/components/heading"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Badge } from "@/components/ui/badge"; - export default function LibraryPage() { const activeTab = useLibraryPageStatesStore(state => state.activeTab); const setActiveTab = useLibraryPageStatesStore(state => state.setActiveTab); @@ -34,7 +33,6 @@ export default function LibraryPage() { const setIsDeleteFileChecked = useDownloadActionStatesStore(state => state.setIsDeleteFileChecked); const { pauseDownload, resumeDownload, cancelDownload } = useAppContext() - const { toast } = useToast(); const queryClient = useQueryClient(); const downloadStateDeleter = useDeleteDownloadState(); @@ -49,24 +47,19 @@ export default function LibraryPage() { if (filePath && await fs.exists(filePath)) { try { await invoke('open_file_with_app', { filePath: filePath, appName: app }).then(() => { - toast({ - title: 'Opening file', + toast.info("Opening file", { description: `Opening the file with ${app ? app : 'default app'}.`, }) }); } catch (e) { console.error(e); - toast({ - title: 'Failed to open file', - description: 'An error occurred while trying to open the file.', - variant: "destructive" + toast.error("Failed to open file", { + description: "An error occurred while trying to open the file.", }) } } else { - toast({ - title: 'File unavailable', - description: 'The file you are trying to open does not exist.', - variant: "destructive" + toast.info("File unavailable", { + description: "The file you are trying to open does not exist.", }) } } @@ -88,18 +81,15 @@ export default function LibraryPage() { onSuccess: (data) => { console.log("Download State deleted successfully:", data); queryClient.invalidateQueries({ queryKey: ['download-states'] }); - toast({ - title: 'Removed from downloads', - description: 'The download has been removed successfully.', - }) + toast.success("Removed from downloads", { + description: "The download has been removed successfully.", + }); }, onError: (error) => { console.error("Failed to delete download state:", error); - toast({ - title: 'Failed to remove download', - description: 'An error occurred while trying to remove the download.', - variant: "destructive" - }) + toast.error("Failed to remove download", { + description: "An error occurred while trying to remove the download.", + }); } }) } @@ -112,26 +102,21 @@ export default function LibraryPage() { await pauseDownload(state); } catch (e) { console.error(e); - toast({ - title: 'Failed to stop download', + toast.error("Failed to stop download", { description: `An error occurred while trying to stop the download for ${state.title}.`, - variant: "destructive" }); } finally { setIsPausingDownload(state.download_id, false); } } if (ongoingDownloads.length === 0) { - toast({ - title: 'Stopped ongoing downloads', - description: 'All ongoing downloads have been stopped successfully.', + toast.success("Stopped ongoing downloads", { + description: "All ongoing downloads have been stopped successfully.", }); } } else { - toast({ - title: 'No ongoing downloads', - description: 'There are no ongoing downloads to stop.', - variant: "destructive" + toast.info("No ongoing downloads", { + description: "There are no ongoing downloads to stop.", }); } } @@ -374,16 +359,13 @@ export default function LibraryPage() { setIsResumingDownload(state.download_id, true); try { await resumeDownload(state) - // toast({ - // title: 'Resumed Download', - // description: 'Download resumed, it will re-start shortly.', + // toast.success("Resumed Download", { + // description: "Download resumed, it will re-start shortly.", // }) } catch (e) { console.error(e); - toast({ - title: 'Failed to Resume Download', - description: 'An error occurred while trying to resume the download.', - variant: "destructive" + toast.error("Failed to Resume Download", { + description: "An error occurred while trying to resume the download.", }) } finally { setIsResumingDownload(state.download_id, false); @@ -411,16 +393,13 @@ export default function LibraryPage() { setIsPausingDownload(state.download_id, true); try { await pauseDownload(state) - // toast({ - // title: 'Paused Download', - // description: 'Download paused successfully.', + // toast.success("Paused Download", { + // description: "Download paused successfully.", // }) } catch (e) { console.error(e); - toast({ - title: 'Failed to Pause Download', - description: 'An error occurred while trying to pause the download.', - variant: "destructive" + toast.error("Failed to Pause Download", { + description: "An error occurred while trying to pause the download." }) } finally { setIsPausingDownload(state.download_id, false); @@ -448,16 +427,13 @@ export default function LibraryPage() { setIsCancelingDownload(state.download_id, true); try { await cancelDownload(state) - toast({ - title: 'Canceled Download', - description: 'Download canceled successfully.', + toast.success("Canceled Download", { + description: "Download canceled successfully.", }) } catch (e) { console.error(e); - toast({ - title: 'Failed to Cancel Download', - description: 'An error occurred while trying to cancel the download.', - variant: "destructive" + toast.error("Failed to Cancel Download", { + description: "An error occurred while trying to cancel the download.", }) } finally { setIsCancelingDownload(state.download_id, false); diff --git a/src/pages/settings.tsx b/src/pages/settings.tsx index 251e6da..dad0b54 100644 --- a/src/pages/settings.tsx +++ b/src/pages/settings.tsx @@ -6,7 +6,7 @@ import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectTrig import { Switch } from "@/components/ui/switch"; import { Label } from "@/components/ui/label"; import { Button } from "@/components/ui/button"; -import { useToast } from "@/hooks/use-toast"; +import { toast } from "sonner"; import { ArrowDownToLine, ArrowRight, BrushCleaning, EthernetPort, ExternalLink, FileVideo, Folder, FolderOpen, Info, Loader2, LucideIcon, Monitor, Moon, Radio, RotateCcw, RotateCw, Sun, Terminal, WandSparkles, Wifi, Wrench } from "lucide-react"; import { cn } from "@/lib/utils"; import { useEffect } from "react"; @@ -65,7 +65,6 @@ const rateLimitSchema = z.object({ }); export default function SettingsPage() { - const { toast } = useToast(); const { setTheme } = useTheme(); const activeTab = useSettingsPageStatesStore(state => state.activeTab); @@ -123,17 +122,14 @@ export default function SettingsPage() { const openLink = async (url: string, app: string | null) => { try { await invoke('open_file_with_app', { filePath: url, appName: app }).then(() => { - toast({ - title: 'Opening Link', + toast.info("Opening link", { description: `Opening link with ${app ? app : 'default app'}.`, }) }); } catch (e) { console.error(e); - toast({ - title: 'Failed to open link', - description: 'An error occurred while trying to open the link.', - variant: "destructive" + toast.error("Failed to open link", { + description: "An error occurred while trying to open the link.", }) } } @@ -148,20 +144,16 @@ export default function SettingsPage() { await fs.remove(filePath); } } - toast({ - title: "Temporary Downloads Cleaned", + toast.success("Temporary Downloads Cleaned", { description: "All temporary downloads have been successfully cleaned up.", }); } catch (e) { - toast({ - title: "Temporary Downloads Cleanup Failed", + toast.error("Temporary Downloads Cleanup Failed", { description: "An error occurred while trying to clean up temporary downloads. Please try again.", - variant: "destructive", }); } } else { - toast({ - title: "No Temporary Downloads", + toast.info("No Temporary Downloads", { description: "There are no temporary downloads to clean up.", }); } @@ -180,16 +172,13 @@ export default function SettingsPage() { function handleProxyUrlSubmit(values: z.infer) { try { saveSettingsKey('proxy_url', values.url); - toast({ - title: "Proxy URL updated", + toast.success("Proxy URL updated", { description: `Proxy URL changed to ${values.url}`, }); } catch (error) { console.error("Error changing proxy URL:", error); - toast({ - title: "Failed to change proxy URL", - description: "Please try again.", - variant: "destructive", + toast.error("Failed to change proxy URL", { + description: "An error occurred while trying to change the proxy URL. Please try again.", }); } } @@ -207,16 +196,13 @@ export default function SettingsPage() { function handleRateLimitSubmit(values: z.infer) { try { saveSettingsKey('rate_limit', values.rate_limit); - toast({ - title: "Rate Limit updated", + toast.success("Rate Limit updated", { description: `Rate Limit changed to ${values.rate_limit} bytes/s`, }); } catch (error) { console.error("Error changing rate limit:", error); - toast({ - title: "Failed to change rate limit", - description: "Please try again.", - variant: "destructive", + toast.error("Failed to change rate limit", { + description: "An error occurred while trying to change the rate limit. Please try again.", }); } } @@ -245,16 +231,13 @@ export default function SettingsPage() { } }); saveSettingsKey('websocket_port', updatedConfig.port); - toast({ - title: "Websocket port updated", + toast.success("Websocket port updated", { description: `Websocket port changed to ${values.port}`, }); } catch (error) { console.error("Error changing websocket port:", error); - toast({ - title: "Failed to change websocket port", - description: "Please try again.", - variant: "destructive", + toast.error("Failed to change websocket port", { + description: "An error occurred while trying to change the websocket port. Please try again.", }); } finally { setIsChangingWebSocketPort(false); @@ -492,10 +475,8 @@ export default function SettingsPage() { } } catch (error) { console.error("Error selecting folder:", error); - toast({ - title: "Failed to select folder", - description: "Please try again.", - variant: "destructive", + toast.error("Failed to select folder", { + description: "An error occurred while trying to select the download folder. Please try again.", }); } }} @@ -739,16 +720,13 @@ export default function SettingsPage() { setIsRestartingWebSocketServer(true); try { await invoke("restart_websocket_server"); - toast({ - title: "Websocket server restarted", + toast.success("Websocket server restarted", { description: "Websocket server restarted successfully.", }); } catch (error) { console.error("Error restarting websocket server:", error); - toast({ - title: "Failed to restart websocket server", - description: "Please try again.", - variant: "destructive", + toast.error("Failed to restart websocket server", { + description: "An error occurred while trying to restart the websocket server. Please try again.", }); } finally { setIsRestartingWebSocketServer(false);