diff --git a/web/src/components/fields/text.tsx b/web/src/components/fields/text.tsx index 1ff15a7..cb7ec5e 100644 --- a/web/src/components/fields/text.tsx +++ b/web/src/components/fields/text.tsx @@ -6,6 +6,8 @@ import { useToggle } from "@hooks/hooks"; import { CheckIcon, DocumentDuplicateIcon, EyeIcon, EyeSlashIcon } from "@heroicons/react/24/outline"; import { useState } from "react"; +import { toast } from "react-hot-toast"; +import Toast from "@components/notifications/Toast"; interface KeyFieldProps { value: string; @@ -30,12 +32,29 @@ export const KeyField = ({ value }: KeyFieldProps) => { .then(() => { // If successful, update the isCopied state value setIsCopied(true); + + toast.custom(t => ( + + )); + setTimeout(() => { setIsCopied(false); }, 1500); }) .catch((err) => { console.error(err); + + toast.custom(t => ( + + )); }); }; diff --git a/web/src/components/notifications/Toast.tsx b/web/src/components/notifications/Toast.tsx index 1b58d1a..27fd449 100644 --- a/web/src/components/notifications/Toast.tsx +++ b/web/src/components/notifications/Toast.tsx @@ -4,12 +4,12 @@ */ import { FC } from "react"; -import { CheckCircleIcon, ExclamationCircleIcon, ExclamationTriangleIcon, XMarkIcon } from "@heroicons/react/24/solid"; +import { CheckCircleIcon, ExclamationCircleIcon, ExclamationTriangleIcon, InformationCircleIcon, XMarkIcon } from "@heroicons/react/24/solid"; import { toast, Toast as Tooast } from "react-hot-toast"; import { classNames } from "@utils"; type Props = { - type: "error" | "success" | "warning" + type: "error" | "success" | "warning" | "info"; body?: string t?: Tooast; }; @@ -24,12 +24,14 @@ const Toast: FC = ({ type, body, t }) => ( {type === "success" && } {type === "error" && } {type === "warning" && } + {type === "info" && } {type === "success" && "Success"} {type === "error" && "Error"} {type === "warning" && "Warning"} + {type === "info" && "Info"} {body} diff --git a/web/src/screens/Logs.tsx b/web/src/screens/Logs.tsx index 0210008..00cd676 100644 --- a/web/src/screens/Logs.tsx +++ b/web/src/screens/Logs.tsx @@ -21,6 +21,9 @@ import { SettingsContext } from "@utils/Context"; import { EmptySimple } from "@components/emptystates"; import { baseUrl } from "@utils"; import { RingResizeSpinner } from "@components/Icons"; +import { toast } from "react-hot-toast"; +import Toast from "@components/notifications/Toast"; + type LogEvent = { time: string; @@ -216,6 +219,12 @@ const LogFilesItem = ({ file }: LogFilesItemProps) => { const handleDownload = async () => { setIsDownloading(true); + + // Add a custom toast before the download starts + const toastId = toast.custom((t) => ( + + )); + const response = await fetch(`${baseUrl()}api/logs/files/${file.filename}`); const blob = await response.blob(); const url = URL.createObjectURL(blob); @@ -224,6 +233,10 @@ const LogFilesItem = ({ file }: LogFilesItemProps) => { link.download = file.filename; link.click(); URL.revokeObjectURL(url); + + // Dismiss the custom toast after the download is complete + toast.dismiss(toastId); + setIsDownloading(false); }; diff --git a/web/src/screens/settings/Application.tsx b/web/src/screens/settings/Application.tsx index 9afe623..60e42a4 100644 --- a/web/src/screens/settings/Application.tsx +++ b/web/src/screens/settings/Application.tsx @@ -100,13 +100,10 @@ function ApplicationSettings() { } }); - const toggleCheckUpdateMutation = useMutation({ - mutationFn: (value: boolean) => APIClient.config.update({ check_for_updates: value }), - onSuccess: () => { - toast.custom((t) => ); - + const toggleCheckUpdateMutation = useMutation((value: boolean) => APIClient.config.update({ check_for_updates: value }).then(() => value), { + onSuccess: (value: boolean) => { + toast.custom(t => ); queryClient.invalidateQueries({ queryKey: ["config"] }); - checkUpdateMutation.mutate(); } }); diff --git a/web/src/screens/settings/DownloadClient.tsx b/web/src/screens/settings/DownloadClient.tsx index fa41b36..66cb81a 100644 --- a/web/src/screens/settings/DownloadClient.tsx +++ b/web/src/screens/settings/DownloadClient.tsx @@ -93,10 +93,9 @@ function DownloadClientSettingsListItem({ client }: DLSettingsItemProps) { const queryClient = useQueryClient(); const mutation = useMutation({ - mutationFn: (client: DownloadClient) => APIClient.download_clients.update(client), - onSuccess: () => { - toast.custom((t) => ); - + mutationFn: (client: DownloadClient) => APIClient.download_clients.update(client).then(() => client), + onSuccess: (client: DownloadClient) => { + toast.custom(t => ); queryClient.invalidateQueries({ queryKey: clientKeys.lists() }); } }); diff --git a/web/src/screens/settings/Feed.tsx b/web/src/screens/settings/Feed.tsx index ff88296..138a035 100644 --- a/web/src/screens/settings/Feed.tsx +++ b/web/src/screens/settings/Feed.tsx @@ -159,7 +159,7 @@ function ListItem({ feed }: ListItemProps) { queryClient.invalidateQueries({ queryKey: feedKeys.lists() }); queryClient.invalidateQueries({ queryKey: feedKeys.detail(feed.id) }); - toast.custom((t) => ); + toast.custom((t) => ); } }); diff --git a/web/src/screens/settings/Irc.tsx b/web/src/screens/settings/Irc.tsx index b4ac56a..7af1e8b 100644 --- a/web/src/screens/settings/Irc.tsx +++ b/web/src/screens/settings/Irc.tsx @@ -219,11 +219,10 @@ const ListItem = ({ idx, network, expanded }: ListItemProps) => { const queryClient = useQueryClient(); const updateMutation = useMutation({ - mutationFn: (network: IrcNetwork) => APIClient.irc.updateNetwork(network), - onSuccess: () => { + mutationFn: (network: IrcNetwork) => APIClient.irc.updateNetwork(network).then(() => network), + onSuccess: (network: IrcNetwork) => { queryClient.invalidateQueries({ queryKey: ircKeys.lists() }); - - toast.custom((t) => ); + toast.custom(t => ); } }); diff --git a/web/src/screens/settings/Notifications.tsx b/web/src/screens/settings/Notifications.tsx index cef8ef7..2647cf1 100644 --- a/web/src/screens/settings/Notifications.tsx +++ b/web/src/screens/settings/Notifications.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: GPL-2.0-or-later */ -import { useQuery } from "@tanstack/react-query"; +import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; import { Switch } from "@headlessui/react"; import { APIClient } from "@api/APIClient"; @@ -12,6 +12,8 @@ import { useToggle } from "@hooks/hooks"; import { NotificationAddForm, NotificationUpdateForm } from "@forms/settings/NotificationForms"; import { classNames } from "@utils"; import { componentMapType } from "@forms/settings/DownloadClientForms"; +import Toast from "@components/notifications/Toast"; +import toast from "react-hot-toast"; export const notificationKeys = { all: ["notifications"] as const, @@ -106,12 +108,29 @@ const iconComponentMap: componentMapType = { }; interface ListItemProps { - notification: Notification; + notification: Notification; } function ListItem({ notification }: ListItemProps) { const [updateFormIsOpen, toggleUpdateForm] = useToggle(false); + const queryClient = useQueryClient(); + + const mutation = useMutation({ + mutationFn: (notification: Notification) => APIClient.notifications.update(notification).then(() => notification), + onSuccess: (notification: Notification) => { + toast.custom(t => ); + queryClient.invalidateQueries({ queryKey: notificationKeys.lists() }); + } + }); + + const onToggleMutation = (newState: boolean) => { + mutation.mutate({ + ...notification, + enabled: newState + }); + }; + return ( @@ -120,7 +139,7 @@ function ListItem({ notification }: ListItemProps) {
{type === "success" && "Success"} {type === "error" && "Error"} {type === "warning" && "Warning"} + {type === "info" && "Info"}