Files
tkmind_go/ui/desktop/src/toasts.tsx
T
Zane 77ea27f5f5 UI update with sidebar and settings tabs (#3288)
Co-authored-by: Nahiyan Khan <nahiyan@squareup.com>
Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Co-authored-by: Lily Delalande <119957291+lily-de@users.noreply.github.com>
Co-authored-by: Spence <spencrmartin@gmail.com>
Co-authored-by: spencrmartin <spencermartin@squareup.com>
Co-authored-by: Judson Stephenson <Jud@users.noreply.github.com>
Co-authored-by: Max Novich <mnovich@squareup.com>
Co-authored-by: Best Codes <106822363+The-Best-Codes@users.noreply.github.com>
Co-authored-by: caroline-a-mckenzie <cmckenzie@squareup.com>
Co-authored-by: Michael Neale <michael.neale@gmail.com>
2025-07-15 17:24:41 -07:00

156 lines
4.0 KiB
TypeScript

import { toast, ToastOptions } from 'react-toastify';
import { Button } from './components/ui/button';
export interface ToastServiceOptions {
silent?: boolean;
shouldThrow?: boolean;
}
export default class ToastService {
private silent: boolean = false;
private shouldThrow: boolean = false;
// Create a singleton instance
private static instance: ToastService;
public static getInstance(): ToastService {
if (!ToastService.instance) {
ToastService.instance = new ToastService();
}
return ToastService.instance;
}
configure(options: ToastServiceOptions = {}): void {
if (options.silent !== undefined) {
this.silent = options.silent;
}
if (options.shouldThrow !== undefined) {
this.shouldThrow = options.shouldThrow;
}
}
error({ title, msg, traceback }: { title: string; msg: string; traceback: string }): void {
if (!this.silent) {
toastError({ title, msg, traceback });
}
if (this.shouldThrow) {
throw new Error(msg);
}
}
loading({ title, msg }: { title: string; msg: string }): string | number | undefined {
if (this.silent) {
return undefined;
}
const toastId = toastLoading({ title, msg });
return toastId;
}
success({ title, msg }: { title: string; msg: string }): void {
if (this.silent) {
return;
}
toastSuccess({ title, msg });
}
dismiss(toastId?: string | number): void {
if (toastId) toast.dismiss(toastId);
}
/**
* Handle errors with consistent logging and toast notifications
* Consolidates the functionality of the original handleError function
*/
handleError(title: string, message: string, options: ToastServiceOptions = {}): void {
this.configure(options);
this.error({
title: title || 'Error',
msg: message,
traceback: message,
});
}
}
// Export a singleton instance for use throughout the app
export const toastService = ToastService.getInstance();
const commonToastOptions: ToastOptions = {
position: 'top-right',
closeButton: true,
hideProgressBar: true,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
};
type ToastSuccessProps = { title?: string; msg?: string; toastOptions?: ToastOptions };
export function toastSuccess({ title, msg, toastOptions = {} }: ToastSuccessProps) {
return toast.success(
<div>
{title ? <strong className="font-medium">{title}</strong> : null}
{title ? <div>{msg}</div> : null}
</div>,
{ ...commonToastOptions, autoClose: 3000, ...toastOptions }
);
}
type ToastErrorProps = {
title?: string;
msg?: string;
traceback?: string;
toastOptions?: ToastOptions;
};
export function toastError({ title, msg, traceback, toastOptions }: ToastErrorProps) {
return toast.error(
<div className="flex gap-4">
<div className="flex-grow">
{title ? <strong className="font-medium">{title}</strong> : null}
{msg ? <div>{msg}</div> : null}
</div>
<div className="flex-none flex items-center">
{traceback ? (
<Button onClick={() => navigator.clipboard.writeText(traceback)}>Copy error</Button>
) : null}
</div>
</div>,
{ ...commonToastOptions, autoClose: traceback ? false : 5000, ...toastOptions }
);
}
type ToastLoadingProps = {
title?: string;
msg?: string;
toastOptions?: ToastOptions;
};
export function toastLoading({ title, msg, toastOptions }: ToastLoadingProps) {
return toast.loading(
<div>
{title ? <strong className="font-medium">{title}</strong> : null}
{title ? <div>{msg}</div> : null}
</div>,
{ ...commonToastOptions, autoClose: false, ...toastOptions }
);
}
type ToastInfoProps = {
title?: string;
msg?: string;
toastOptions?: ToastOptions;
};
export function toastInfo({ title, msg, toastOptions }: ToastInfoProps) {
return toast.info(
<div>
{title ? <strong className="font-medium">{title}</strong> : null}
{msg ? <div>{msg}</div> : null}
</div>,
{ ...commonToastOptions, ...toastOptions }
);
}