You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
105 lines
2.4 KiB
105 lines
2.4 KiB
import { useState, useEffect } from 'react'; |
|
|
|
import { useIntl } from 'react-intl'; |
|
import type { IntlShape } from 'react-intl'; |
|
|
|
import classNames from 'classnames'; |
|
|
|
import { dismissAlert } from 'mastodon/actions/alerts'; |
|
import type { |
|
Alert, |
|
TranslatableString, |
|
TranslatableValues, |
|
} from 'mastodon/models/alert'; |
|
import { useAppSelector, useAppDispatch } from 'mastodon/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> |
|
); |
|
};
|
|
|