mirror of
https://github.com/idanoo/autobrr
synced 2025-07-23 08:49:13 +00:00
enhancement(web): mutation improvements and toast updates (#913)
* make notification switch take onToggleMutation Instead of opening it like the edit button, it now enables/disables it directly. * improved toast for update checks * improved toast for download clients it now mentions what client is enabled/disabled * improved irc network toast * added toast when copying apikey * added toast to log download implemented an info variant for the toasts * improved feed toast * improved toast for update checks * Merge branch 'develop' into enhancement/mutation-improvements-toast-updates
This commit is contained in:
parent
96e38e649a
commit
8acf33589d
8 changed files with 68 additions and 20 deletions
|
@ -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 => (
|
||||
<Toast
|
||||
type="success"
|
||||
body="API key copied to clipboard!"
|
||||
t={t}
|
||||
/>
|
||||
));
|
||||
|
||||
setTimeout(() => {
|
||||
setIsCopied(false);
|
||||
}, 1500);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
|
||||
toast.custom(t => (
|
||||
<Toast
|
||||
type="error"
|
||||
body="Failed to copy API key."
|
||||
t={t}
|
||||
/>
|
||||
));
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -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<Props> = ({ type, body, t }) => (
|
|||
{type === "success" && <CheckCircleIcon className="h-6 w-6 text-green-400" aria-hidden="true" />}
|
||||
{type === "error" && <ExclamationCircleIcon className="h-6 w-6 text-red-400" aria-hidden="true" />}
|
||||
{type === "warning" && <ExclamationTriangleIcon className="h-6 w-6 text-yellow-400" aria-hidden="true" />}
|
||||
{type === "info" && <InformationCircleIcon className="h-6 w-6 text-blue-400" aria-hidden="true" />}
|
||||
</div>
|
||||
<div className="ml-3 w-0 flex-1 pt-0.5">
|
||||
<p className="text-sm font-medium text-gray-900 dark:text-gray-200">
|
||||
{type === "success" && "Success"}
|
||||
{type === "error" && "Error"}
|
||||
{type === "warning" && "Warning"}
|
||||
{type === "info" && "Info"}
|
||||
</p>
|
||||
<span className="mt-1 text-sm text-gray-500 dark:text-gray-400">{body}</span>
|
||||
</div>
|
||||
|
|
|
@ -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) => (
|
||||
<Toast type="info" body="Log file is being sanitized. Please wait..." t={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);
|
||||
};
|
||||
|
||||
|
|
|
@ -100,13 +100,10 @@ function ApplicationSettings() {
|
|||
}
|
||||
});
|
||||
|
||||
const toggleCheckUpdateMutation = useMutation({
|
||||
mutationFn: (value: boolean) => APIClient.config.update({ check_for_updates: value }),
|
||||
onSuccess: () => {
|
||||
toast.custom((t) => <Toast type="success" body={"Config successfully updated!"} t={t}/>);
|
||||
|
||||
const toggleCheckUpdateMutation = useMutation((value: boolean) => APIClient.config.update({ check_for_updates: value }).then(() => value), {
|
||||
onSuccess: (value: boolean) => {
|
||||
toast.custom(t => <Toast type="success" body={`${value ? "You will now be notified of new updates." : "You will no longer be notified of new updates."}`} t={t} />);
|
||||
queryClient.invalidateQueries({ queryKey: ["config"] });
|
||||
|
||||
checkUpdateMutation.mutate();
|
||||
}
|
||||
});
|
||||
|
|
|
@ -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) => <Toast type="success" body={`${client.name} was updated successfully`} t={t}/>);
|
||||
|
||||
mutationFn: (client: DownloadClient) => APIClient.download_clients.update(client).then(() => client),
|
||||
onSuccess: (client: DownloadClient) => {
|
||||
toast.custom(t => <Toast type="success" body={`${client.name} was ${client.enabled ? "enabled" : "disabled"} successfully.`} t={t} />);
|
||||
queryClient.invalidateQueries({ queryKey: clientKeys.lists() });
|
||||
}
|
||||
});
|
||||
|
|
|
@ -159,7 +159,7 @@ function ListItem({ feed }: ListItemProps) {
|
|||
queryClient.invalidateQueries({ queryKey: feedKeys.lists() });
|
||||
queryClient.invalidateQueries({ queryKey: feedKeys.detail(feed.id) });
|
||||
|
||||
toast.custom((t) => <Toast type="success" body={`${feed.name} was ${!enabled ? "disabled" : "enabled"} successfully`} t={t}/>);
|
||||
toast.custom((t) => <Toast type="success" body={`${feed.name} was ${!enabled ? "disabled" : "enabled"} successfully.`} t={t}/>);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -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 type="success" body={`${network.name} was updated successfully`} t={t}/>);
|
||||
toast.custom(t => <Toast type="success" body={`${network.name} was ${network.enabled ? "enabled" : "disabled"} successfully.`} t={t} />);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
@ -112,6 +114,23 @@ interface ListItemProps {
|
|||
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 => <Toast type="success" body={`${notification.name} was ${notification.enabled ? "enabled" : "disabled"} successfully.`} t={t} />);
|
||||
queryClient.invalidateQueries({ queryKey: notificationKeys.lists() });
|
||||
}
|
||||
});
|
||||
|
||||
const onToggleMutation = (newState: boolean) => {
|
||||
mutation.mutate({
|
||||
...notification,
|
||||
enabled: newState
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<li key={notification.id} className="text-gray-500 dark:text-gray-400">
|
||||
<NotificationUpdateForm isOpen={updateFormIsOpen} toggle={toggleUpdateForm} notification={notification} />
|
||||
|
@ -120,7 +139,7 @@ function ListItem({ notification }: ListItemProps) {
|
|||
<div className="col-span-2 sm:col-span-1 px-6 flex items-center ">
|
||||
<Switch
|
||||
checked={notification.enabled}
|
||||
onChange={toggleUpdateForm}
|
||||
onChange={onToggleMutation}
|
||||
className={classNames(
|
||||
notification.enabled ? "bg-blue-500" : "bg-gray-200 dark:bg-gray-600",
|
||||
"relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue