The code powering m.abunchtell.com https://m.abunchtell.com
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.
 
 
 
 

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