import { useNuiEvent } from '../../hooks/useNuiEvent'; import { toast, Toaster } from 'react-hot-toast'; import ReactMarkdown from 'react-markdown'; import { Box, Center, createStyles, Group, keyframes, RingProgress, Stack, Text, ThemeIcon } from '@mantine/core'; import React, { useState } from 'react'; import tinycolor from 'tinycolor2'; import type { NotificationProps } from '../../typings'; import MarkdownComponents from '../../config/MarkdownComponents'; import LibIcon from '../../components/LibIcon'; const useStyles = createStyles((theme) => ({ container: { width: 300, height: 'fit-content', backgroundColor: theme.colors.dark[6], color: theme.colors.dark[0], padding: 12, borderRadius: theme.radius.sm, fontFamily: 'Roboto', boxShadow: theme.shadows.sm, }, title: { fontWeight: 500, lineHeight: 'normal', }, description: { fontSize: 12, color: theme.colors.dark[2], fontFamily: 'Roboto', lineHeight: 'normal', }, descriptionOnly: { fontSize: 14, color: theme.colors.dark[2], fontFamily: 'Roboto', lineHeight: 'normal', }, })); const createAnimation = (from: string, to: string, visible: boolean) => keyframes({ from: { opacity: visible ? 0 : 1, transform: `translate${from}`, }, to: { opacity: visible ? 1 : 0, transform: `translate${to}`, }, }); const getAnimation = (visible: boolean, position: string) => { const animationOptions = visible ? '0.2s ease-out forwards' : '0.4s ease-in forwards' let animation: { from: string; to: string }; if (visible) { animation = position.includes('bottom') ? { from: 'Y(30px)', to: 'Y(0px)' } : { from: 'Y(-30px)', to:'Y(0px)' }; } else { if (position.includes('right')) { animation = { from: 'X(0px)', to: 'X(100%)' } } else if (position.includes('left')) { animation = { from: 'X(0px)', to: 'X(-100%)' }; } else if (position === 'top-center') { animation = { from: 'Y(0px)', to: 'Y(-100%)' }; } else if (position === 'bottom-center') { animation = { from: 'Y(0px)', to: 'Y(100%)' }; } else { animation = { from: 'X(0px)', to: 'X(100%)' }; } } return `${createAnimation(animation.from, animation.to, visible)} ${animationOptions}` }; const durationCircle = keyframes({ '0%': { strokeDasharray: `0, ${15.1 * 2 * Math.PI}` }, '100%': { strokeDasharray: `${15.1 * 2 * Math.PI}, 0` }, }); const Notifications: React.FC = () => { const { classes } = useStyles(); const [toastKey, setToastKey] = useState(0); useNuiEvent('notify', (data) => { if (!data.title && !data.description) return; const toastId = data.id?.toString(); const duration = data.duration || 3000; let iconColor: string; let position = data.position || 'top-right'; data.showDuration = data.showDuration !== undefined ? data.showDuration : true; if (toastId) setToastKey(prevKey => prevKey + 1); // Backwards compat with old notifications switch (position) { case 'top': position = 'top-center'; break; case 'bottom': position = 'bottom-center'; break; } if (!data.icon) { switch (data.type) { case 'error': data.icon = 'circle-xmark'; break; case 'success': data.icon = 'circle-check'; break; case 'warning': data.icon = 'circle-exclamation'; break; default: data.icon = 'circle-info'; break; } } if (!data.iconColor) { switch (data.type) { case 'error': iconColor = 'red.6'; break; case 'success': iconColor = 'teal.6'; break; case 'warning': iconColor = 'yellow.6'; break; default: iconColor = 'blue.6'; break; } } else { iconColor = tinycolor(data.iconColor).toRgbString(); } toast.custom( (t) => ( {data.icon && ( <> {data.showDuration ? ( svg > circle:nth-of-type(2)': { animation: `${durationCircle} linear forwards reverse`, animationDuration: `${duration}ms`, }, margin: -3, }, }} label={
} /> ) : ( )} )} {data.title && {data.title}} {data.description && ( {data.description} )}
), { id: toastId, duration: duration, position: position, } ); }); return ; }; export default Notifications;