diff --git a/packages/react/src/components/Button/Button.js b/packages/react/src/components/Button/Button.js
index 0405dc8a04..2d8e823c8a 100644
--- a/packages/react/src/components/Button/Button.js
+++ b/packages/react/src/components/Button/Button.js
@@ -96,7 +96,7 @@ const Button = ({
}
&.ghost {
background: none;
- color: ${theme?.palette?.[color]?.main || '#1A2027'};
+ color: ${theme?.palette?.[color]?.main || 'currentColor'};
border: none;
}
&.disabled.ghost {
diff --git a/packages/react/src/components/ChatInput/ChatInput.js b/packages/react/src/components/ChatInput/ChatInput.js
index 613c328c5c..924a2887f1 100644
--- a/packages/react/src/components/ChatInput/ChatInput.js
+++ b/packages/react/src/components/ChatInput/ChatInput.js
@@ -5,7 +5,6 @@ import React, {
useEffect,
useCallback,
} from 'react';
-import { useToastBarDispatch } from '@rocket.chat/fuselage-toastbar';
import { css } from '@emotion/react';
import styles from './ChatInput.module.css';
import RCContext from '../../context/RCInstance';
@@ -29,6 +28,7 @@ import { Icon } from '../Icon';
import { CommandsList } from '../CommandList';
import { ActionButton } from '../ActionButton';
import useComponentOverrides from '../../theme/useComponentOverrides';
+import { useToastBarDispatch } from '../../hooks/useToastBarDispatch';
const ChatInput = () => {
const { styleOverrides, classNames } = useComponentOverrides('ChatInput');
@@ -158,7 +158,6 @@ const ChatInput = () => {
dispatchToastMessage({
type: 'error',
message: 'Error sending message, login again',
- position: toastPosition,
});
} else {
replaceMessage(pendingMessage._id, res.message);
diff --git a/packages/react/src/components/Icon/Icon.js b/packages/react/src/components/Icon/Icon.js
index 1e56484e99..c26f6dffea 100644
--- a/packages/react/src/components/Icon/Icon.js
+++ b/packages/react/src/components/Icon/Icon.js
@@ -24,7 +24,7 @@ const Icon = ({ size = 24, name, className = '', style = {}, ...props }) => {
y="0"
width={size}
height={size}
- color="inherit"
+ color="currentColor"
className={`ec-icon ${classNames}`}
style={styleOverrides}
{...props}
diff --git a/packages/react/src/components/Icon/icons/Check.js b/packages/react/src/components/Icon/icons/Check.js
new file mode 100644
index 0000000000..c5974b1196
--- /dev/null
+++ b/packages/react/src/components/Icon/icons/Check.js
@@ -0,0 +1,14 @@
+import React from 'react';
+
+const Check = (props) => (
+
+);
+
+export default Check;
diff --git a/packages/react/src/components/Icon/icons/ErrorCircle.js b/packages/react/src/components/Icon/icons/ErrorCircle.js
new file mode 100644
index 0000000000..b5bd3ef6b5
--- /dev/null
+++ b/packages/react/src/components/Icon/icons/ErrorCircle.js
@@ -0,0 +1,14 @@
+import React from 'react';
+
+const ErrorCircle = (props) => (
+
+);
+
+export default ErrorCircle;
diff --git a/packages/react/src/components/Icon/icons/index.js b/packages/react/src/components/Icon/icons/index.js
index c15aa924bb..d4eb698527 100644
--- a/packages/react/src/components/Icon/icons/index.js
+++ b/packages/react/src/components/Icon/icons/index.js
@@ -32,6 +32,8 @@ import Italic from './Italic';
import StarFilled from './StarFilled';
import Trash from './Trash';
import Kebab from './Kebab';
+import Check from './Check';
+import ErrorCircle from './ErrorCircle';
const icons = {
file: File,
@@ -68,6 +70,8 @@ const icons = {
'star-filled': StarFilled,
trash: Trash,
kebab: Kebab,
+ check: Check,
+ 'error-circle': ErrorCircle,
};
export default icons;
diff --git a/packages/react/src/components/Message/Message.js b/packages/react/src/components/Message/Message.js
index 539d36dcdc..237e33a23b 100644
--- a/packages/react/src/components/Message/Message.js
+++ b/packages/react/src/components/Message/Message.js
@@ -1,7 +1,6 @@
import React, { useContext, useMemo } from 'react';
import PropTypes from 'prop-types';
import Cookies from 'js-cookie';
-import { useToastBarDispatch } from '@rocket.chat/fuselage-toastbar';
import { format } from 'date-fns';
import { css } from '@emotion/react';
import { Attachments } from '../Attachments';
@@ -22,6 +21,7 @@ import { MessageMetrics } from './MessageMetrics';
import { MessageToolbox } from './MessageToolbox';
import { Avatar } from '../Avatar';
import { MessageDivider } from './MessageDivider';
+import { useToastBarDispatch } from '../../hooks/useToastBarDispatch';
const MessageCss = css`
display: flex;
@@ -77,14 +77,12 @@ const Message = ({
dispatchToastMessage({
type: 'success',
message: 'Message starred',
- position: toastPosition,
});
} else {
await RCInstance.unstarMessage(msg._id);
dispatchToastMessage({
type: 'success',
message: 'Message unstarred',
- position: toastPosition,
});
}
};
@@ -98,13 +96,11 @@ const Message = ({
dispatchToastMessage({
type: 'error',
message: 'Error pinning message',
- position: toastPosition,
});
} else {
dispatchToastMessage({
type: 'success',
message: isPinned ? 'Message unpinned' : 'Message pinned',
- position: toastPosition,
});
}
};
@@ -116,13 +112,11 @@ const Message = ({
dispatchToastMessage({
type: 'success',
message: 'Message deleted successfully',
- position: toastPosition,
});
} else {
dispatchToastMessage({
type: 'error',
message: 'Error in deleting message',
- position: toastPosition,
});
}
};
diff --git a/packages/react/src/components/ReportMessage/ReportWindowButtons.js b/packages/react/src/components/ReportMessage/ReportWindowButtons.js
index 0cf9fb8e7e..ac28ccf84d 100644
--- a/packages/react/src/components/ReportMessage/ReportWindowButtons.js
+++ b/packages/react/src/components/ReportMessage/ReportWindowButtons.js
@@ -1,11 +1,11 @@
import React, { useContext } from 'react';
-import { useToastBarDispatch } from '@rocket.chat/fuselage-toastbar';
import PropTypes from 'prop-types';
import { useMessageStore, useToastStore } from '../../store';
import RCContext from '../../context/RCInstance';
import { Button } from '../Button';
import { Icon } from '../Icon';
import { Modal } from '../Modal';
+import { useToastBarDispatch } from '../../hooks/useToastBarDispatch';
const ReportWindowButtons = ({ children, reportDescription, messageId }) => {
const [toggleReportMessage, setMessageToReport] = useMessageStore((state) => [
@@ -28,13 +28,11 @@ const ReportWindowButtons = ({ children, reportDescription, messageId }) => {
dispatchToastMessage({
type: 'success',
message: 'Message reported successfully',
- position: toastPosition,
});
} else {
dispatchToastMessage({
type: 'error',
message: 'Error in reporting message',
- position: toastPosition,
});
}
diff --git a/packages/react/src/components/ToastBar/ToastBar.js b/packages/react/src/components/ToastBar/ToastBar.js
new file mode 100644
index 0000000000..ddbba2af52
--- /dev/null
+++ b/packages/react/src/components/ToastBar/ToastBar.js
@@ -0,0 +1,86 @@
+/* eslint-disable no-shadow */
+import React, { useMemo, useRef, useEffect } from 'react';
+import { css, useTheme, keyframes } from '@emotion/react';
+import alpha from 'color-alpha';
+import { appendClassNames } from '../../lib/appendClassNames';
+import useComponentOverrides from '../../theme/useComponentOverrides';
+import { Box } from '../Box';
+import { Icon } from '../Icon';
+import { ActionButton } from '../ActionButton';
+
+const ToastBar = ({ toast, onClose }) => {
+ const { type, message, time = 2000 } = toast;
+ const toastRef = useRef();
+ const theme = useTheme();
+ const { classNames, styleOverrides } = useComponentOverrides('ToastBar');
+ const { iconName, bgColor, color } = useMemo(() => {
+ const color = theme.palette?.[type]?.main;
+ const bgColor = alpha(color, 0.3);
+ let iconName = 'success';
+ switch (type) {
+ case 'info':
+ iconName = 'info';
+ break;
+ case 'warning':
+ iconName = 'report';
+ break;
+ case 'error':
+ iconName = 'error-circle';
+ break;
+ case 'success':
+ default:
+ iconName = 'check';
+ }
+ return { iconName, color, bgColor };
+ }, [theme.palette, type]);
+
+ const animation = keyframes`
+ 0% {
+ opacity: 0;
+ }
+ 20% {
+ opacity: 1;
+ }
+ 80% {
+ opacity: 1;
+ }
+ 100% {
+ opacity: 0;
+ }
+ `;
+
+ const ToastBarCSS = css`
+ display: flex;
+ flex-direction: row;
+ gap: 1em;
+ align-items: center;
+ justify-content: space-between;
+ width: fit-content;
+ max-width: 20rem;
+ color: ${color};
+ background-color: ${bgColor};
+ border-radius: 0.25em;
+ padding: 0.75em 1em;
+ z-index: ${theme.zIndex?.toastbar};
+ animation: ${animation} ${time}ms ease-in-out forwards;
+ `;
+
+ useEffect(() => {
+ setTimeout(onClose, time);
+ }, [onClose, time]);
+
+ return (
+
+
+ {message}
+
+
+ );
+};
+
+export default ToastBar;
diff --git a/packages/react/src/components/ToastBar/ToastBar.stories.js b/packages/react/src/components/ToastBar/ToastBar.stories.js
new file mode 100644
index 0000000000..8ec057aa13
--- /dev/null
+++ b/packages/react/src/components/ToastBar/ToastBar.stories.js
@@ -0,0 +1,76 @@
+import React from 'react';
+import { ThemeProvider } from '@emotion/react';
+import { ToastBar, ToastBarProvider } from '.';
+import DefaultTheme from '../../theme/DefaultTheme';
+import { Button } from '../Button';
+import { useToastBarDispatch } from '../../hooks/useToastBarDispatch';
+
+export default {
+ title: 'Components/ToastBar',
+ component: ToastBar,
+};
+
+const ToastBarContainer = () => {
+ const dispatchToast = useToastBarDispatch();
+ return (
+ <>
+
+
+
+
+ >
+ );
+};
+
+const Template = (args) => (
+
+
+
+
+
+);
+
+export const Default = Template.bind({});
+Default.args = {
+ position: 'bottom right',
+};
diff --git a/packages/react/src/components/ToastBar/ToastBarProvider.js b/packages/react/src/components/ToastBar/ToastBarProvider.js
new file mode 100644
index 0000000000..f0f9062ebf
--- /dev/null
+++ b/packages/react/src/components/ToastBar/ToastBarProvider.js
@@ -0,0 +1,30 @@
+import React, { useState, useCallback, useMemo } from 'react';
+import ToastContext from '../../context/ToastContext';
+import ToastContainer from './ToastContainer';
+
+const ToastBarProvider = ({ position = 'bottom left', children }) => {
+ const [toasts, setToasts] = useState([]);
+ const dispatchToast = useCallback(
+ (toast) => {
+ setToasts((prevToasts) => [toast, ...prevToasts]);
+ },
+ [setToasts]
+ );
+ const contextValue = useMemo(
+ () => ({
+ toasts,
+ dispatchToast,
+ position,
+ setToasts,
+ }),
+ [toasts, dispatchToast, position, setToasts]
+ );
+ return (
+
+ {children}
+
+
+ );
+};
+
+export default ToastBarProvider;
diff --git a/packages/react/src/components/ToastBar/ToastContainer.js b/packages/react/src/components/ToastBar/ToastContainer.js
new file mode 100644
index 0000000000..078a30ecac
--- /dev/null
+++ b/packages/react/src/components/ToastBar/ToastContainer.js
@@ -0,0 +1,41 @@
+import React, { useContext, useMemo, useCallback } from 'react';
+import { css, useTheme } from '@emotion/react';
+import ToastContext from '../../context/ToastContext';
+import { Box } from '../Box';
+import ToastBar from './ToastBar';
+
+const ToastContainer = () => {
+ const theme = useTheme();
+ const ToastContainerCss = css`
+ position: fixed;
+ z-index: ${theme.zIndex.toastbar};
+ `;
+
+ const { position, toasts, setToasts } = useContext(ToastContext);
+ const positionStyle = useMemo(() => {
+ const positions = position.split(/\s+/);
+ const styleAnchor = {};
+ positions.forEach((pos) => {
+ styleAnchor[pos] = `2rem`;
+ });
+ return styleAnchor;
+ }, [position]);
+ const currentToast = useMemo(() => {
+ const toast = toasts[toasts.length - 1];
+ return toast;
+ }, [toasts]);
+
+ const onClose = useCallback(() => {
+ setToasts(toasts.slice(0, toasts.length - 1));
+ }, [setToasts, toasts]);
+ if (!currentToast) {
+ return null;
+ }
+ return (
+
+
+
+ );
+};
+
+export default ToastContainer;
diff --git a/packages/react/src/components/ToastBar/index.js b/packages/react/src/components/ToastBar/index.js
new file mode 100644
index 0000000000..1e572ca23d
--- /dev/null
+++ b/packages/react/src/components/ToastBar/index.js
@@ -0,0 +1,2 @@
+export { default as ToastBar } from './ToastBar';
+export { default as ToastBarProvider } from './ToastBarProvider';
diff --git a/packages/react/src/context/ToastContext.js b/packages/react/src/context/ToastContext.js
new file mode 100644
index 0000000000..6e12c1de9d
--- /dev/null
+++ b/packages/react/src/context/ToastContext.js
@@ -0,0 +1,5 @@
+import { createContext } from 'react';
+
+const ToastContext = createContext();
+
+export default ToastContext;
diff --git a/packages/react/src/hooks/useRCAuth.js b/packages/react/src/hooks/useRCAuth.js
index 7ac08b8f23..2b4eec957f 100644
--- a/packages/react/src/hooks/useRCAuth.js
+++ b/packages/react/src/hooks/useRCAuth.js
@@ -1,5 +1,4 @@
import { useContext } from 'react';
-import { useToastBarDispatch } from '@rocket.chat/fuselage-toastbar';
import RCContext from '../context/RCInstance';
import {
useToastStore,
@@ -7,6 +6,7 @@ import {
totpModalStore,
loginModalStore,
} from '../store';
+import { useToastBarDispatch } from './useToastBarDispatch';
export const useRCAuth = () => {
const { RCInstance } = useContext(RCContext);
diff --git a/packages/react/src/hooks/useRCAuth4Google.js b/packages/react/src/hooks/useRCAuth4Google.js
index 6386c20f82..19a2ce3101 100644
--- a/packages/react/src/hooks/useRCAuth4Google.js
+++ b/packages/react/src/hooks/useRCAuth4Google.js
@@ -1,8 +1,8 @@
import { useContext } from 'react';
-import { useToastBarDispatch } from '@rocket.chat/fuselage-toastbar';
import RCContext from '../context/RCInstance';
import { useGoogleLogin } from './useGoogleLogin';
import { useToastStore, useUserStore, totpModalStore } from '../store';
+import { useToastBarDispatch } from './useToastBarDispatch';
export const useRCAuth4Google = (GOOGLE_CLIENT_ID) => {
const { signIn } = useGoogleLogin(GOOGLE_CLIENT_ID);
diff --git a/packages/react/src/hooks/useToastBarDispatch.js b/packages/react/src/hooks/useToastBarDispatch.js
new file mode 100644
index 0000000000..c34abf5053
--- /dev/null
+++ b/packages/react/src/hooks/useToastBarDispatch.js
@@ -0,0 +1,7 @@
+import { useContext } from 'react';
+import ToastContext from '../context/ToastContext';
+
+export const useToastBarDispatch = () => {
+ const { dispatchToast } = useContext(ToastContext);
+ return dispatchToast;
+};
diff --git a/packages/react/src/index.js b/packages/react/src/index.js
index 069a0e54ad..b75b5e770c 100644
--- a/packages/react/src/index.js
+++ b/packages/react/src/index.js
@@ -1,6 +1,5 @@
import React, { useEffect, useMemo, useState } from 'react';
import PropTypes from 'prop-types';
-import { ToastBarProvider } from '@rocket.chat/fuselage-toastbar';
// eslint-disable-next-line import/no-extraneous-dependencies
import { css, ThemeProvider } from '@emotion/react';
import { EmbeddedChatApi } from '@embeddedchat/api';
@@ -13,6 +12,7 @@ import DefaultTheme from './theme/DefaultTheme';
import { deleteToken, getToken, saveToken } from './lib/auth';
import { Box } from './components/Box';
import useComponentOverrides from './theme/useComponentOverrides';
+import { ToastBarProvider } from './components/ToastBar';
export const EmbeddedChat = ({
isClosable = false,
@@ -25,7 +25,7 @@ export const EmbeddedChat = ({
channelName,
anonymousMode = false,
headerColor = '#fff',
- toastBarPosition = 'bottom-end',
+ toastBarPosition = 'bottom right',
showRoles = false,
showAvatar = false,
enableThreads = false,
@@ -136,7 +136,7 @@ export const EmbeddedChat = ({
return (
-
+
{attachmentWindowOpen ? : null}