Repository: ui
Issue: [bug]: Toasts stop being dismissed correctly if one of them is dismissed with an action
Labels: bug

### Describe the bug

Hello! I found out something weird. If you dismiss a toast with an action, the others(if you have a limit bigger then 1) are no longer being dismiss.


Expected behavior: 

After I interact with a Toast/Notification, the timeout for dismissal should be resumed. That is never happening.

Also...after a mouse interaction(`mouseenter`) the dismissal should be cleared.


I have fixed my issue by adding a `dismissQueue`(alongside the `removeQueue` that useToast already has).


```

const addToDismissQueue = (toastId: string) => {
  if (dismissQueue.has(toastId)) return;
  const timeout = setTimeout(() => {
    dismissQueue.delete(toastId);
    dispatch({ type: 'DISMISS_TOAST', toastId });
  }, TOAST_GLOBALS.duration);

  dismissQueue.set(toastId, timeout);
};

const removeFromDismissQueue = (toastId: string) => {
  clearTimeout(dismissQueue.get(toastId));
  dismissQueue.delete(toastId);
};

const clearDismissQueue = () =>{
  for(const timeout of dismissQueue.values()){
    clearTimeout(timeout);
  }
  dismissQueue.clear();
}

```

And when I dispatch the `ADD_TOAST`, I place the props:

```
dispatch(
    {
      type: 'ADD_TOAST',
      toast: {
        ...props,
        onMouseLeave: ()=> addToDismissQueue(id),
        onMouseEnter: ()=> removeFromDismissQueue(id),
        duration,
        id,
        open: true,
        onOpenChange: open => {
          if (!open) dismiss();
        },
      },
    }
  );

```


Don't forget to clear the dismissQueue when you clear all notifications:

```
export const reducer = (state: State, action: Action): State => {
  switch (action.type) {
    case 'ADD_TOAST':
      // addToDismissQueue(action.toast.id, duration, limit);
      return {
        ...state,
        toasts: [action.toast, ...state.toasts].slice(0, TOAST_GLOBALS.limit),
      };

    case 'UPDATE_TOAST':
      return {
        ...state,
        toasts: state.toasts.map(t => (t.id === action.toast.id ? { ...t, ...action.toast } : t)),
      };

    case '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 'REMOVE_TOAST':
      if (action.toastId === undefined) {
        clearDismissQueue();
        return {
          ...state,
          toasts: [],
        };
      }
      return {
        ...state,
        toasts: state.toasts.filter(t => t.id !== action.toastId),
      };
  }
};
```

This what we achieve the desired outcome.


Note:

The `TOAST_GLOBALS` constant is something I came up with in order to set the parameters from a context right inside the `useToast` hook:


```
function useToast() {
  const context = React.useContext(ToasterContext);
  if (!context) throw new Error('useToast must be used inside the Toaster context');
  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]);
  Object.assign(TOAST_GLOBALS, context);
  return {
    ...state,
    toast,
    unmount: () => {
      dispatch({ type: 'REMOVE_TOAST' });
      setState(memoryState);
    },
    dismiss: (toastId?: string) => {
      dispatch({ type: 'DISMISS_TOAST', toastId });
      if (!toastId) return;
      for (const toast of memoryState.toasts.filter(toast => toast.id !== toastId)) {
        addToDismissQueue(toast.id);
      }
    },
    context,
  };
}

```

I don't think this approach is optimal so I am posting it here to temporary solve this issue(at least for me it was an issue) and I am waiting for a fix in the near future. Thanks!

### Affected component/components

Toast

### How to reproduce

1. Add basic toast to project
2. Update the delay and the limit (limit = 3 and delay = 3000)
3. Add the Toaster to your project
4. Add a button that creates a toast
5. Add an action inside the toast that dismisses the toast(or use the dismiss function that is returned from the useToast hook)
6. Press the `insert-toast` button twice and dismiss one of the toasts.
7. The second toast is never dismissed


Extra: You can add other toasts and they won't be dismissed either.

### Codesandbox/StackBlitz link

https://codesandbox.io/p/devbox/4sknl5

### Logs

_No response_

### System Info

```bash
Systems: Linux / Mac / Windows
Browsers: Chrome / Arc / Firefox / Zen
```


### Before submitting

- [x] I've made research efforts and searched the documentation
- [x] I've searched for existing issues