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.

177 lines
6.3 KiB

  1. import React from 'react';
  2. import PropTypes from 'prop-types';
  3. import ImmutablePropTypes from 'react-immutable-proptypes';
  4. import StatusContainer from '../../../containers/status_container';
  5. import AccountContainer from '../../../containers/account_container';
  6. import { injectIntl, FormattedMessage } from 'react-intl';
  7. import Permalink from '../../../components/permalink';
  8. import ImmutablePureComponent from 'react-immutable-pure-component';
  9. import { HotKeys } from 'react-hotkeys';
  10. const notificationForScreenReader = (intl, message, timestamp) => {
  11. const output = [message];
  12. output.push(intl.formatDate(timestamp, { hour: '2-digit', minute: '2-digit', month: 'short', day: 'numeric' }));
  13. return output.join(', ');
  14. };
  15. export default @injectIntl
  16. class Notification extends ImmutablePureComponent {
  17. static contextTypes = {
  18. router: PropTypes.object,
  19. };
  20. static propTypes = {
  21. notification: ImmutablePropTypes.map.isRequired,
  22. hidden: PropTypes.bool,
  23. onMoveUp: PropTypes.func.isRequired,
  24. onMoveDown: PropTypes.func.isRequired,
  25. onMention: PropTypes.func.isRequired,
  26. intl: PropTypes.object.isRequired,
  27. };
  28. handleMoveUp = () => {
  29. const { notification, onMoveUp } = this.props;
  30. onMoveUp(notification.get('id'));
  31. }
  32. handleMoveDown = () => {
  33. const { notification, onMoveDown } = this.props;
  34. onMoveDown(notification.get('id'));
  35. }
  36. handleOpen = () => {
  37. const { notification } = this.props;
  38. if (notification.get('status')) {
  39. this.context.router.history.push(`/statuses/${notification.get('status')}`);
  40. } else {
  41. this.handleOpenProfile();
  42. }
  43. }
  44. handleOpenProfile = () => {
  45. const { notification } = this.props;
  46. this.context.router.history.push(`/accounts/${notification.getIn(['account', 'id'])}`);
  47. }
  48. handleMention = e => {
  49. e.preventDefault();
  50. const { notification, onMention } = this.props;
  51. onMention(notification.get('account'), this.context.router.history);
  52. }
  53. getHandlers () {
  54. return {
  55. moveUp: this.handleMoveUp,
  56. moveDown: this.handleMoveDown,
  57. open: this.handleOpen,
  58. openProfile: this.handleOpenProfile,
  59. mention: this.handleMention,
  60. reply: this.handleMention,
  61. };
  62. }
  63. renderFollow (notification, account, link) {
  64. const { intl } = this.props;
  65. return (
  66. <HotKeys handlers={this.getHandlers()}>
  67. <div className='notification notification-follow focusable' tabIndex='0' aria-label={notificationForScreenReader(intl, intl.formatMessage({ id: 'notification.follow', defaultMessage: '{name} followed you' }, { name: account.get('acct') }), notification.get('created_at'))}>
  68. <div className='notification__message'>
  69. <div className='notification__favourite-icon-wrapper'>
  70. <i className='fa fa-fw fa-user-plus' />
  71. </div>
  72. <span title={notification.get('created_at')}>
  73. <FormattedMessage id='notification.follow' defaultMessage='{name} followed you' values={{ name: link }} />
  74. </span>
  75. </div>
  76. <AccountContainer id={account.get('id')} withNote={false} hidden={this.props.hidden} />
  77. </div>
  78. </HotKeys>
  79. );
  80. }
  81. renderMention (notification) {
  82. return (
  83. <StatusContainer
  84. id={notification.get('status')}
  85. withDismiss
  86. hidden={this.props.hidden}
  87. onMoveDown={this.handleMoveDown}
  88. onMoveUp={this.handleMoveUp}
  89. contextType='notifications'
  90. />
  91. );
  92. }
  93. renderFavourite (notification, link) {
  94. const { intl } = this.props;
  95. return (
  96. <HotKeys handlers={this.getHandlers()}>
  97. <div className='notification notification-favourite focusable' tabIndex='0' aria-label={notificationForScreenReader(intl, intl.formatMessage({ id: 'notification.favourite', defaultMessage: '{name} favourited your status' }, { name: notification.getIn(['account', 'acct']) }), notification.get('created_at'))}>
  98. <div className='notification__message'>
  99. <div className='notification__favourite-icon-wrapper'>
  100. <i className='fa fa-fw fa-star star-icon' />
  101. </div>
  102. <span title={notification.get('created_at')}>
  103. <FormattedMessage id='notification.favourite' defaultMessage='{name} favourited your status' values={{ name: link }} />
  104. </span>
  105. </div>
  106. <StatusContainer id={notification.get('status')} account={notification.get('account')} muted withDismiss hidden={!!this.props.hidden} />
  107. </div>
  108. </HotKeys>
  109. );
  110. }
  111. renderReblog (notification, link) {
  112. const { intl } = this.props;
  113. return (
  114. <HotKeys handlers={this.getHandlers()}>
  115. <div className='notification notification-reblog focusable' tabIndex='0' aria-label={notificationForScreenReader(intl, intl.formatMessage({ id: 'notification.reblog', defaultMessage: '{name} boosted your status' }, { name: notification.getIn(['account', 'acct']) }), notification.get('created_at'))}>
  116. <div className='notification__message'>
  117. <div className='notification__favourite-icon-wrapper'>
  118. <i className='fa fa-fw fa-retweet' />
  119. </div>
  120. <span title={notification.get('created_at')}>
  121. <FormattedMessage id='notification.reblog' defaultMessage='{name} boosted your status' values={{ name: link }} />
  122. </span>
  123. </div>
  124. <StatusContainer id={notification.get('status')} account={notification.get('account')} muted withDismiss hidden={this.props.hidden} />
  125. </div>
  126. </HotKeys>
  127. );
  128. }
  129. render () {
  130. const { notification } = this.props;
  131. const account = notification.get('account');
  132. const displayNameHtml = { __html: account.get('display_name_html') };
  133. const link = <bdi><Permalink className='notification__display-name' href={account.get('url')} title={account.get('acct')} to={`/accounts/${account.get('id')}`} dangerouslySetInnerHTML={displayNameHtml} /></bdi>;
  134. switch(notification.get('type')) {
  135. case 'follow':
  136. return this.renderFollow(notification, account, link);
  137. case 'mention':
  138. return this.renderMention(notification);
  139. case 'favourite':
  140. return this.renderFavourite(notification, link);
  141. case 'reblog':
  142. return this.renderReblog(notification, link);
  143. }
  144. return null;
  145. }
  146. }