Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(web/notify): changed useRef to useState to prevent resetting the circle on notifications without an ID and organized the code. #566

Merged
merged 1 commit into from
May 2, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 42 additions & 84 deletions web/src/features/notifications/NotificationWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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}` },
Expand All @@ -111,22 +78,22 @@ const durationCircle = keyframes({

const Notifications: React.FC = () => {
const { classes } = useStyles();
const toastKeyRef = useRef(0);
const [toastKey, setToastKey] = useState(0);

useNuiEvent<NotificationProps>('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';
Expand All @@ -135,6 +102,7 @@ const Notifications: React.FC = () => {
position = 'bottom-center';
break;
}

if (!data.icon) {
switch (data.type) {
case 'error':
Expand All @@ -151,6 +119,7 @@ const Notifications: React.FC = () => {
break;
}
}

if (!data.iconColor) {
switch (data.type) {
case 'error':
Expand All @@ -169,23 +138,12 @@ const Notifications: React.FC = () => {
} else {
iconColor = tinycolor(data.iconColor).toRgbString();
}

toast.custom(
(t) => (
<Box
sx={{
animation: t.visible
? `${position?.includes('bottom') ? enterAnimationBottom : enterAnimationTop} 0.2s ease-out forwards`
: `${
position?.includes('right')
? exitAnimationRight
: position?.includes('left')
? exitAnimationLeft
: position === 'top-center'
? exitAnimationTop
: position
? exitAnimationBottom
: exitAnimationRight
} 0.4s ease-in forwards`,
animation: getAnimation(t.visible, position),
...data.style,
}}
className={`${classes.container}`}
Expand All @@ -195,7 +153,7 @@ const Notifications: React.FC = () => {
<>
{data.showDuration ? (
<RingProgress
key={toastKeyRef.current}
key={toastKey}
size={38}
thickness={2}
sections={[{ value: 100, color: iconColor }]}
Expand All @@ -215,7 +173,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'}
>
<LibIcon icon={data.icon} fixedWidth color={iconColor} animation={data.iconAnimation} />
</ThemeIcon>
Expand All @@ -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' }}
>
<LibIcon icon={data.icon} fixedWidth color={iconColor} animation={data.iconAnimation} />
Expand All @@ -252,7 +210,7 @@ const Notifications: React.FC = () => {
{
id: toastId,
duration: duration,
position: position || 'top-right',
position: position,
}
);
});
Expand Down