glitch-soc/app/javascript/flavours/glitch/components/alerts_controller.tsx

106 lines
2.4 KiB
TypeScript

import { useState, useEffect } from 'react';
import { useIntl } from 'react-intl';
import type { IntlShape } from 'react-intl';
import classNames from 'classnames';
import { dismissAlert } from 'flavours/glitch/actions/alerts';
import type {
Alert,
TranslatableString,
TranslatableValues,
} from 'flavours/glitch/models/alert';
import { useAppSelector, useAppDispatch } from 'flavours/glitch/store';
const formatIfNeeded = (
intl: IntlShape,
message: TranslatableString,
values?: TranslatableValues,
) => {
if (typeof message === 'object') {
return intl.formatMessage(message, values);
}
return message;
};
const Alert: React.FC<{
alert: Alert;
dismissAfter: number;
}> = ({
alert: { key, title, message, values, action, onClick },
dismissAfter,
}) => {
const dispatch = useAppDispatch();
const intl = useIntl();
const [active, setActive] = useState(false);
useEffect(() => {
const setActiveTimeout = setTimeout(() => {
setActive(true);
}, 1);
return () => {
clearTimeout(setActiveTimeout);
};
}, []);
useEffect(() => {
const dismissTimeout = setTimeout(() => {
setActive(false);
// Allow CSS transition to finish before removing from the DOM
setTimeout(() => {
dispatch(dismissAlert({ key }));
}, 500);
}, dismissAfter);
return () => {
clearTimeout(dismissTimeout);
};
}, [dispatch, setActive, key, dismissAfter]);
return (
<div
className={classNames('notification-bar', {
'notification-bar-active': active,
})}
>
<div className='notification-bar-wrapper'>
{title && (
<span className='notification-bar-title'>
{formatIfNeeded(intl, title, values)}
</span>
)}
<span className='notification-bar-message'>
{formatIfNeeded(intl, message, values)}
</span>
{action && (
<button className='notification-bar-action' onClick={onClick}>
{formatIfNeeded(intl, action, values)}
</button>
)}
</div>
</div>
);
};
export const AlertsController: React.FC = () => {
const alerts = useAppSelector((state) => state.alerts);
if (alerts.length === 0) {
return null;
}
return (
<div className='notification-list'>
{alerts.map((alert, idx) => (
<Alert key={alert.key} alert={alert} dismissAfter={5000 + idx * 1000} />
))}
</div>
);
};