pleroma/app/soapbox/features/notifications/components/notification.js

246 lines
6.5 KiB
JavaScript
Raw Normal View History

2020-03-27 13:59:38 -07:00
import PropTypes from 'prop-types';
import React from 'react';
import { HotKeys } from 'react-hotkeys';
2020-03-27 13:59:38 -07:00
import ImmutablePropTypes from 'react-immutable-proptypes';
2022-03-21 11:09:01 -07:00
import { FormattedMessage, useIntl } from 'react-intl';
2022-03-22 05:42:26 -07:00
import { useHistory } from 'react-router-dom';
2022-03-21 11:09:01 -07:00
import Icon from '../../../components/icon';
2022-01-10 14:01:24 -08:00
import Permalink from '../../../components/permalink';
2022-03-21 11:09:01 -07:00
import { HStack, Text } from '../../../components/ui';
import AccountContainer from '../../../containers/account_container';
import StatusContainer from '../../../containers/status_container';
2020-03-27 13:59:38 -07:00
const notificationForScreenReader = (intl, message, timestamp) => {
const output = [message];
output.push(intl.formatDate(timestamp, { hour: '2-digit', minute: '2-digit', month: 'short', day: 'numeric' }));
return output.join(', ');
};
2022-03-21 11:09:01 -07:00
// Workaround for dynamic messages (https://github.com/formatjs/babel-plugin-react-intl/issues/119#issuecomment-326202499)
function FormattedMessageFixed(props) {
return <FormattedMessage {...props} />;
}
2020-03-27 13:59:38 -07:00
2022-03-21 11:09:01 -07:00
const buildLink = (account) => (
<bdi>
<Permalink
className='text-gray-800 font-bold hover:underline'
href={`/@${account.get('acct')}`}
title={account.get('acct')}
to={`/@${account.get('acct')}`}
dangerouslySetInnerHTML={{ __html: account.get('display_name_html') }}
/>
</bdi>
);
export const NOTIFICATION_TYPES = ['follow', 'mention', 'favourite', 'reblog'];
const icons = {
2022-03-21 12:05:52 -07:00
follow: require('@tabler/icons/icons/user-plus.svg'),
mention: require('@tabler/icons/icons/at.svg'),
favourite: require('@tabler/icons/icons/heart.svg'),
reblog: require('@tabler/icons/icons/repeat.svg'),
2022-03-21 11:09:01 -07:00
};
2020-03-27 13:59:38 -07:00
2022-03-21 11:09:01 -07:00
const messages = {
follow: {
id: 'notification.follow',
defaultMessage: '{name} followed you',
},
mention: {
id: 'notification.mentioned',
defaultMessage: '{name} mentioned you',
},
favourite: {
id: 'notification.favourite',
defaultMessage: '{name} liked your TRUTH',
},
reblog: {
id: 'notification.reblog',
defaultMessage: '{name} re-TRUTH your TRUTH',
},
};
2020-03-27 13:59:38 -07:00
2022-03-21 11:09:01 -07:00
const buildMessage = (type, account) => {
const link = buildLink(account);
2020-03-27 13:59:38 -07:00
2022-03-21 11:09:01 -07:00
return (
<FormattedMessageFixed
id={messages[type].id}
defaultMessage={messages[type].defaultMessage}
values={{ name: link }}
/>
);
};
2020-03-27 13:59:38 -07:00
2022-03-21 11:09:01 -07:00
const Notification = (props) => {
2022-03-22 05:42:26 -07:00
const { hidden, notification, onMoveUp, onMoveDown } = props;
2022-03-21 11:09:01 -07:00
2022-03-22 05:42:26 -07:00
const history = useHistory();
2022-03-21 11:09:01 -07:00
const intl = useIntl();
2022-03-22 05:42:26 -07:00
2022-03-21 11:09:01 -07:00
const type = notification.get('type');
const timestamp = notification.get('created_at');
const account = notification.get('account');
const getHandlers = () => ({
reply: handleMention,
favourite: handleHotkeyFavourite,
boost: handleHotkeyBoost,
mention: handleMention,
open: handleOpen,
openProfile: handleOpenProfile,
moveUp: handleMoveUp,
moveDown: handleMoveDown,
toggleHidden: handleHotkeyToggleHidden,
});
const handleOpen = () => {
2020-03-27 13:59:38 -07:00
if (notification.get('status')) {
2022-03-21 11:09:01 -07:00
history.push(`/@${notification.getIn(['account', 'acct'])}/posts/${notification.getIn(['status', 'id'])}`);
2020-03-27 13:59:38 -07:00
} else {
2022-03-21 11:09:01 -07:00
handleOpenProfile();
2020-03-27 13:59:38 -07:00
}
2022-03-21 11:09:01 -07:00
};
2020-03-27 13:59:38 -07:00
2022-03-21 11:09:01 -07:00
const handleOpenProfile = () => {
history.push(`/@${notification.getIn(['account', 'acct'])}`);
};
2020-03-27 13:59:38 -07:00
2022-03-21 11:09:01 -07:00
const handleMention = (event) => {
event.preventDefault();
2020-03-27 13:59:38 -07:00
2022-03-21 11:09:01 -07:00
props.onMention(notification.get('account'), history);
};
2020-03-27 13:59:38 -07:00
2022-03-21 11:09:01 -07:00
const handleHotkeyFavourite = () => {
const status = notification.get('status');
2022-03-21 11:09:01 -07:00
if (status) props.onFavourite(status);
};
2020-03-27 13:59:38 -07:00
2022-03-21 11:09:01 -07:00
const handleHotkeyBoost = (e) => {
const status = notification.get('status');
2022-03-21 11:09:01 -07:00
if (status) props.onReblog(status, e);
};
2020-03-27 13:59:38 -07:00
2022-03-21 11:09:01 -07:00
const handleHotkeyToggleHidden = () => {
const status = notification.get('status');
2022-03-21 11:09:01 -07:00
if (status) props.onToggleHidden(status);
};
2022-03-21 11:09:01 -07:00
const handleMoveUp = () => {
onMoveUp(notification.get('id'));
};
2022-03-21 11:09:01 -07:00
const handleMoveDown = () => {
onMoveDown(notification.get('id'));
};
2022-03-21 11:09:01 -07:00
const renderContent = () => {
switch (type) {
case 'follow':
return (
<AccountContainer
id={notification.getIn(['account', 'id'])}
withNote={false}
hidden={hidden}
avatarSize={48}
/>
);
case 'favourite':
case 'mention':
case 'reblog':
return (
2021-10-05 20:01:16 -07:00
<StatusContainer
id={notification.getIn(['status', 'id'])}
2021-10-05 20:01:16 -07:00
withDismiss
2022-03-21 11:09:01 -07:00
hidden={hidden}
onMoveDown={handleMoveDown}
onMoveUp={handleMoveUp}
2021-10-05 20:01:16 -07:00
contextType='notifications'
2022-03-21 11:09:01 -07:00
getScrollPosition={props.getScrollPosition}
updateScrollBottom={props.updateScrollBottom}
cachedMediaWidth={props.cachedMediaWidth}
cacheMediaWidth={props.cacheMediaWidth}
2021-10-05 20:01:16 -07:00
/>
2022-03-21 11:09:01 -07:00
);
default:
return null;
}
};
2020-03-27 13:59:38 -07:00
2022-03-21 11:09:01 -07:00
if (!NOTIFICATION_TYPES.includes(type)) {
return null;
2020-03-27 13:59:38 -07:00
}
2022-03-21 11:09:01 -07:00
const message = buildMessage(type, account);
return (
<HotKeys handlers={getHandlers()}>
<div
className='notification focusable'
tabIndex='0'
aria-label={
notificationForScreenReader(
intl,
intl.formatMessage({
id: messages[type].id,
defaultMessage: messages[type].defaultMessage,
},
{
name: notification.getIn(['account', 'acct']),
}),
notification.get('created_at'),
)
}
>
<div className='p-4 focusable'>
<div className='mb-2'>
<HStack alignItems='center' space={1.5}>
<Icon
2022-03-21 12:05:52 -07:00
src={icons[type]}
2022-03-21 11:09:01 -07:00
className='text-primary-600'
/>
<div>
<Text
theme='muted'
size='sm'
title={timestamp}
>
{message}
</Text>
</div>
</HStack>
2020-03-27 13:59:38 -07:00
</div>
2022-03-21 11:09:01 -07:00
<div>
{renderContent()}
</div>
</div>
2022-03-21 11:09:01 -07:00
</div>
</HotKeys>
);
};
2020-03-27 13:59:38 -07:00
2022-03-21 11:09:01 -07:00
Notification.propTypes = {
hidden: PropTypes.bool,
notification: ImmutablePropTypes.map.isRequired,
onMoveUp: PropTypes.func.isRequired,
onMoveDown: PropTypes.func.isRequired,
onMention: PropTypes.func.isRequired,
onFavourite: PropTypes.func.isRequired,
onReblog: PropTypes.func.isRequired,
onToggleHidden: PropTypes.func.isRequired,
getScrollPosition: PropTypes.func,
updateScrollBottom: PropTypes.func,
cacheMediaWidth: PropTypes.func,
cachedMediaWidth: PropTypes.number,
siteTitle: PropTypes.string,
};
2020-03-27 13:59:38 -07:00
2022-03-22 05:42:26 -07:00
export default Notification;