diff --git a/web/src/features/notifications/NotificationWrapper.tsx b/web/src/features/notifications/NotificationWrapper.tsx index 4659727f0..44a770f10 100644 --- a/web/src/features/notifications/NotificationWrapper.tsx +++ b/web/src/features/notifications/NotificationWrapper.tsx @@ -2,7 +2,7 @@ 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, { useRef } from 'react'; +import React, { useState } from 'react'; import tinycolor from 'tinycolor2'; import type { NotificationProps } from '../../typings'; import MarkdownComponents from '../../config/MarkdownComponents'; @@ -37,72 +37,39 @@ const useStyles = createStyles((theme) => ({ }, })); -// I hate this -const enterAnimationTop = keyframes({ +const createAnimation = (from: string, to: string, visible: boolean) => keyframes({ from: { - opacity: 0, - transform: 'translateY(-30px)', + opacity: visible ? 0 : 1, + transform: `translate${from}`, }, to: { - opacity: 1, - transform: 'translateY(0px)', + opacity: visible ? 1 : 0, + transform: `translate${to}`, }, }); -const enterAnimationBottom = keyframes({ - from: { - opacity: 0, - transform: 'translateY(30px)', - }, - to: { - opacity: 1, - transform: 'translateY(0px)', - }, -}); - -const exitAnimationTop = keyframes({ - from: { - opacity: 1, - transform: 'translateY(0px)', - }, - to: { - opacity: 0, - transform: 'translateY(-100%)', - }, -}); - -const exitAnimationRight = keyframes({ - from: { - opacity: 1, - transform: 'translateX(0px)', - }, - to: { - opacity: 0, - transform: 'translateX(100%)', - }, -}); - -const exitAnimationLeft = keyframes({ - from: { - opacity: 1, - transform: 'translateX(0px)', - }, - to: { - opacity: 0, - transform: 'translateX(-100%)', - }, -}); +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') { + animation = { from: 'Y(0px)', to: 'Y(100%)' }; + } else { + animation = { from: 'X(0px)', to: 'X(100%)' }; + } + } -const exitAnimationBottom = keyframes({ - from: { - opacity: 1, - transform: 'translateY(0px)', - }, - to: { - opacity: 0, - transform: 'translateY(100%)', - }, -}); + return `${createAnimation(animation.from, animation.to, visible)} ${animationOptions}` +}; const durationCircle = keyframes({ '0%': { strokeDasharray: `0, ${15.1 * 2 * Math.PI}` }, @@ -111,22 +78,22 @@ const durationCircle = keyframes({ const Notifications: React.FC = () => { const { classes } = useStyles(); - const toastKeyRef = useRef(0); + const [toastKey, setToastKey] = useState(0); useNuiEvent('notify', (data) => { - const toastId = data.id?.toString(); - - if (toastId) toastKeyRef.current++; - if (!data.title && !data.description) return; + const toastId = data.id?.toString(); + const duration = data.duration || 3000; + let iconColor: string; - let duration = data.duration || 3000; + 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 - let position = data.position; switch (position) { case 'top': position = 'top-center'; @@ -135,6 +102,7 @@ const Notifications: React.FC = () => { position = 'bottom-center'; break; } + if (!data.icon) { switch (data.type) { case 'error': @@ -151,6 +119,7 @@ const Notifications: React.FC = () => { break; } } + if (!data.iconColor) { switch (data.type) { case 'error': @@ -169,23 +138,12 @@ const Notifications: React.FC = () => { } else { iconColor = tinycolor(data.iconColor).toRgbString(); } + toast.custom( (t) => ( { <> {data.showDuration ? ( { color={iconColor} radius="xl" size={32} - variant={tinycolor(iconColor).getAlpha() === 0 ? undefined : 'light'} + variant={tinycolor(iconColor).getAlpha() < 0 ? undefined : 'light'} > @@ -227,7 +185,7 @@ const Notifications: React.FC = () => { color={iconColor} radius="xl" size={32} - variant={tinycolor(iconColor).getAlpha() === 0 ? undefined : 'light'} + variant={tinycolor(iconColor).getAlpha() < 0 ? undefined : 'light'} style={{ alignSelf: !data.alignIcon || data.alignIcon === 'center' ? 'center' : 'start' }} > @@ -252,7 +210,7 @@ const Notifications: React.FC = () => { { id: toastId, duration: duration, - position: position || 'top-right', + position: position, } ); });