Files
inspector/client/src/hooks/use-toast.ts
KavyapriyaJG eab6b42ac6 Fix - Lint warnings and errors in client
Lint issues fixed:
1. Removed unused error variable in catch block
2. Changed 'ActionTypes' type to enum
3. Removed unnecessary interface extensions
4. Removed unused export component
5. Added the missing dependencies to useMemo
6. Fixed the inline function issue by changing it to useMemo
2025-04-12 02:10:37 +05:30

192 lines
3.9 KiB
TypeScript

"use client";
// Inspired by react-hot-toast library
import * as React from "react";
import type { ToastActionElement, ToastProps } from "@/components/ui/toast";
const TOAST_LIMIT = 1;
const TOAST_REMOVE_DELAY = 1000000;
type ToasterToast = ToastProps & {
id: string;
title?: React.ReactNode;
description?: React.ReactNode;
action?: ToastActionElement;
};
let count = 0;
function genId() {
count = (count + 1) % Number.MAX_SAFE_INTEGER;
return count.toString();
}
const enum ActionType {
ADD_TOAST = "ADD_TOAST",
UPDATE_TOAST = "UPDATE_TOAST",
DISMISS_TOAST = "DISMISS_TOAST",
REMOVE_TOAST = "REMOVE_TOAST",
}
type Action =
| {
type: ActionType.ADD_TOAST;
toast: ToasterToast;
}
| {
type: ActionType.UPDATE_TOAST;
toast: Partial<ToasterToast>;
}
| {
type: ActionType.DISMISS_TOAST;
toastId?: ToasterToast["id"];
}
| {
type: ActionType.REMOVE_TOAST;
toastId?: ToasterToast["id"];
};
interface State {
toasts: ToasterToast[];
}
const toastTimeouts = new Map<string, ReturnType<typeof setTimeout>>();
const addToRemoveQueue = (toastId: string) => {
if (toastTimeouts.has(toastId)) {
return;
}
const timeout = setTimeout(() => {
toastTimeouts.delete(toastId);
dispatch({
type: ActionType.REMOVE_TOAST,
toastId: toastId,
});
}, TOAST_REMOVE_DELAY);
toastTimeouts.set(toastId, timeout);
};
export const reducer = (state: State, action: Action): State => {
switch (action.type) {
case ActionType.ADD_TOAST:
return {
...state,
toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT),
};
case ActionType.UPDATE_TOAST:
return {
...state,
toasts: state.toasts.map((t) =>
t.id === action.toast.id ? { ...t, ...action.toast } : t,
),
};
case ActionType.DISMISS_TOAST: {
const { toastId } = action;
// ! Side effects ! - This could be extracted into a dismissToast() action,
// but I'll keep it here for simplicity
if (toastId) {
addToRemoveQueue(toastId);
} else {
state.toasts.forEach((toast) => {
addToRemoveQueue(toast.id);
});
}
return {
...state,
toasts: state.toasts.map((t) =>
t.id === toastId || toastId === undefined
? {
...t,
open: false,
}
: t,
),
};
}
case ActionType.REMOVE_TOAST:
if (action.toastId === undefined) {
return {
...state,
toasts: [],
};
}
return {
...state,
toasts: state.toasts.filter((t) => t.id !== action.toastId),
};
}
};
const listeners: Array<(state: State) => void> = [];
let memoryState: State = { toasts: [] };
function dispatch(action: Action) {
memoryState = reducer(memoryState, action);
listeners.forEach((listener) => {
listener(memoryState);
});
}
type Toast = Omit<ToasterToast, "id">;
function toast({ ...props }: Toast) {
const id = genId();
const update = (props: ToasterToast) =>
dispatch({
type: ActionType.UPDATE_TOAST,
toast: { ...props, id },
});
const dismiss = () =>
dispatch({ type: ActionType.DISMISS_TOAST, toastId: id });
dispatch({
type: ActionType.ADD_TOAST,
toast: {
...props,
id,
open: true,
onOpenChange: (open) => {
if (!open) dismiss();
},
},
});
return {
id: id,
dismiss,
update,
};
}
function useToast() {
const [state, setState] = React.useState<State>(memoryState);
React.useEffect(() => {
listeners.push(setState);
return () => {
const index = listeners.indexOf(setState);
if (index > -1) {
listeners.splice(index, 1);
}
};
}, [state]);
return {
...state,
toast,
dismiss: (toastId?: string) =>
dispatch({ type: ActionType.DISMISS_TOAST, toastId }),
};
}
export { useToast, toast };