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.

191 lines
4.5 KiB

  1. /*
  2. `<StatusPrepend>`
  3. =================
  4. Originally a part of `<Status>`, but extracted into a separate
  5. component for better documentation and maintainance by
  6. @kibi@glitch.social as a part of glitch-soc/mastodon.
  7. */
  8. /* * * * */
  9. /*
  10. Imports:
  11. --------
  12. */
  13. // Package imports //
  14. import React from 'react';
  15. import PropTypes from 'prop-types';
  16. import ImmutablePropTypes from 'react-immutable-proptypes';
  17. import escapeTextContentForBrowser from 'escape-html';
  18. import { defineMessages, injectIntl } from 'react-intl';
  19. import { FormattedMessage } from 'react-intl';
  20. // Mastodon imports //
  21. import emojify from '../../../mastodon/emoji';
  22. const messages = defineMessages({
  23. deleteNotification: { id: 'status.dismiss_notification', defaultMessage: 'Dismiss notification' },
  24. });
  25. /* * * * */
  26. /*
  27. The `<StatusPrepend>` component:
  28. --------------------------------
  29. The `<StatusPrepend>` component holds a status's prepend, ie the text
  30. that says X reblogged this, etc. It is represented by an `<aside>`
  31. element.
  32. ### Props
  33. - __`type` (`PropTypes.string`) :__
  34. The type of prepend. One of `'reblogged_by'`, `'reblog'`,
  35. `'favourite'`.
  36. - __`account` (`ImmutablePropTypes.map`) :__
  37. The account associated with the prepend.
  38. - __`parseClick` (`PropTypes.func.isRequired`) :__
  39. Our click parsing function.
  40. */
  41. @injectIntl
  42. export default class StatusPrepend extends React.PureComponent {
  43. static propTypes = {
  44. type: PropTypes.string.isRequired,
  45. account: ImmutablePropTypes.map.isRequired,
  46. parseClick: PropTypes.func.isRequired,
  47. notificationId: PropTypes.number,
  48. onDeleteNotification: PropTypes.func,
  49. intl: PropTypes.object.isRequired,
  50. };
  51. /*
  52. ### Implementation
  53. #### `handleClick()`.
  54. This is just a small wrapper for `parseClick()` that gets fired when
  55. an account link is clicked.
  56. */
  57. handleClick = (e) => {
  58. const { account, parseClick } = this.props;
  59. parseClick(e, `/accounts/${+account.get('id')}`);
  60. }
  61. handleNotificationDeleteClick = () => {
  62. this.props.onDeleteNotification(this.props.notificationId);
  63. }
  64. /*
  65. #### `<Message>`.
  66. `<Message>` is a quick functional React component which renders the
  67. actual prepend message based on our provided `type`. First we create a
  68. `link` for the account's name, and then use `<FormattedMessage>` to
  69. generate the message.
  70. */
  71. Message = () => {
  72. const { type, account } = this.props;
  73. let link = (
  74. <a
  75. onClick={this.handleClick}
  76. href={account.get('url')}
  77. className='status__display-name'
  78. >
  79. <b
  80. dangerouslySetInnerHTML={{
  81. __html : emojify(escapeTextContentForBrowser(
  82. account.get('display_name') || account.get('username')
  83. )),
  84. }}
  85. />
  86. </a>
  87. );
  88. switch (type) {
  89. case 'reblogged_by':
  90. return (
  91. <FormattedMessage
  92. id='status.reblogged_by'
  93. defaultMessage='{name} boosted'
  94. values={{ name : link }}
  95. />
  96. );
  97. case 'favourite':
  98. return (
  99. <FormattedMessage
  100. id='notification.favourite'
  101. defaultMessage='{name} favourited your status'
  102. values={{ name : link }}
  103. />
  104. );
  105. case 'reblog':
  106. return (
  107. <FormattedMessage
  108. id='notification.reblog'
  109. defaultMessage='{name} boosted your status'
  110. values={{ name : link }}
  111. />
  112. );
  113. }
  114. return null;
  115. }
  116. /*
  117. #### `render()`.
  118. Our `render()` is incredibly simple; we just render the icon and then
  119. the `<Message>` inside of an <aside>.
  120. */
  121. render () {
  122. const { Message } = this;
  123. const { type, intl } = this.props;
  124. const dismissTitle = intl.formatMessage(messages.deleteNotification);
  125. const dismiss = this.props.notificationId ? (
  126. <button
  127. aria-label={dismissTitle}
  128. title={dismissTitle}
  129. onClick={this.handleNotificationDeleteClick}
  130. className='status__prepend-dismiss-button'
  131. >
  132. <i className='fa fa-eraser' />
  133. </button>
  134. ) : null;
  135. return !type ? null : (
  136. <aside className={type === 'reblogged_by' ? 'status__prepend' : 'notification__message'}>
  137. <div className={type === 'reblogged_by' ? 'status__prepend-icon-wrapper' : 'notification__favourite-icon-wrapper'}>
  138. <i
  139. className={`fa fa-fw fa-${
  140. type === 'favourite' ? 'star star-icon' : 'retweet'
  141. } status__prepend-icon`}
  142. />
  143. </div>
  144. <Message />
  145. {dismiss}
  146. </aside>
  147. );
  148. }
  149. }