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.

137 lines
5.2 KiB

  1. import React from 'react';
  2. import { connect } from 'react-redux';
  3. import ImmutablePureComponent from 'react-immutable-pure-component';
  4. import ImmutablePropTypes from 'react-immutable-proptypes';
  5. import PropTypes from 'prop-types';
  6. import IconButton from 'mastodon/components/icon_button';
  7. import classNames from 'classnames';
  8. import { me, boostModal } from 'mastodon/initial_state';
  9. import { defineMessages, injectIntl } from 'react-intl';
  10. import { replyCompose } from 'mastodon/actions/compose';
  11. import { reblog, favourite, unreblog, unfavourite } from 'mastodon/actions/interactions';
  12. import { makeGetStatus } from 'mastodon/selectors';
  13. import { openModal } from 'mastodon/actions/modal';
  14. const messages = defineMessages({
  15. reply: { id: 'status.reply', defaultMessage: 'Reply' },
  16. replyAll: { id: 'status.replyAll', defaultMessage: 'Reply to thread' },
  17. reblog: { id: 'status.reblog', defaultMessage: 'Boost' },
  18. reblog_private: { id: 'status.reblog_private', defaultMessage: 'Boost with original visibility' },
  19. cancel_reblog_private: { id: 'status.cancel_reblog_private', defaultMessage: 'Unboost' },
  20. cannot_reblog: { id: 'status.cannot_reblog', defaultMessage: 'This post cannot be boosted' },
  21. favourite: { id: 'status.favourite', defaultMessage: 'Favourite' },
  22. replyConfirm: { id: 'confirmations.reply.confirm', defaultMessage: 'Reply' },
  23. replyMessage: { id: 'confirmations.reply.message', defaultMessage: 'Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?' },
  24. });
  25. const makeMapStateToProps = () => {
  26. const getStatus = makeGetStatus();
  27. const mapStateToProps = (state, { statusId }) => ({
  28. status: getStatus(state, { id: statusId }),
  29. askReplyConfirmation: state.getIn(['compose', 'text']).trim().length !== 0,
  30. });
  31. return mapStateToProps;
  32. };
  33. export default @connect(makeMapStateToProps)
  34. @injectIntl
  35. class Footer extends ImmutablePureComponent {
  36. static contextTypes = {
  37. router: PropTypes.object,
  38. };
  39. static propTypes = {
  40. statusId: PropTypes.string.isRequired,
  41. status: ImmutablePropTypes.map.isRequired,
  42. intl: PropTypes.object.isRequired,
  43. dispatch: PropTypes.func.isRequired,
  44. askReplyConfirmation: PropTypes.bool,
  45. };
  46. _performReply = () => {
  47. const { dispatch, status } = this.props;
  48. dispatch(replyCompose(status, this.context.router.history));
  49. };
  50. handleReplyClick = () => {
  51. const { dispatch, askReplyConfirmation, intl } = this.props;
  52. if (askReplyConfirmation) {
  53. dispatch(openModal('CONFIRM', {
  54. message: intl.formatMessage(messages.replyMessage),
  55. confirm: intl.formatMessage(messages.replyConfirm),
  56. onConfirm: this._performReply,
  57. }));
  58. } else {
  59. this._performReply();
  60. }
  61. };
  62. handleFavouriteClick = () => {
  63. const { dispatch, status } = this.props;
  64. if (status.get('favourited')) {
  65. dispatch(unfavourite(status));
  66. } else {
  67. dispatch(favourite(status));
  68. }
  69. };
  70. _performReblog = () => {
  71. const { dispatch, status } = this.props;
  72. dispatch(reblog(status));
  73. }
  74. handleReblogClick = e => {
  75. const { dispatch, status } = this.props;
  76. if (status.get('reblogged')) {
  77. dispatch(unreblog(status));
  78. } else if ((e && e.shiftKey) || !boostModal) {
  79. this._performReblog();
  80. } else {
  81. dispatch(openModal('BOOST', { status, onReblog: this._performReblog }));
  82. }
  83. };
  84. render () {
  85. const { status, intl } = this.props;
  86. const publicStatus = ['public', 'unlisted'].includes(status.get('visibility'));
  87. const reblogPrivate = status.getIn(['account', 'id']) === me && status.get('visibility') === 'private';
  88. let replyIcon, replyTitle;
  89. if (status.get('in_reply_to_id', null) === null) {
  90. replyIcon = 'reply';
  91. replyTitle = intl.formatMessage(messages.reply);
  92. } else {
  93. replyIcon = 'reply-all';
  94. replyTitle = intl.formatMessage(messages.replyAll);
  95. }
  96. let reblogTitle = '';
  97. if (status.get('reblogged')) {
  98. reblogTitle = intl.formatMessage(messages.cancel_reblog_private);
  99. } else if (publicStatus) {
  100. reblogTitle = intl.formatMessage(messages.reblog);
  101. } else if (reblogPrivate) {
  102. reblogTitle = intl.formatMessage(messages.reblog_private);
  103. } else {
  104. reblogTitle = intl.formatMessage(messages.cannot_reblog);
  105. }
  106. return (
  107. <div className='picture-in-picture__footer'>
  108. <IconButton className='status__action-bar-button' title={replyTitle} icon={status.get('in_reply_to_account_id') === status.getIn(['account', 'id']) ? 'reply' : replyIcon} onClick={this.handleReplyClick} counter={status.get('replies_count')} obfuscateCount />
  109. <IconButton className={classNames('status__action-bar-button', { reblogPrivate })} disabled={!publicStatus && !reblogPrivate} active={status.get('reblogged')} pressed={status.get('reblogged')} title={reblogTitle} icon='retweet' onClick={this.handleReblogClick} counter={status.get('reblogs_count')} />
  110. <IconButton className='status__action-bar-button star-icon' animate active={status.get('favourited')} pressed={status.get('favourited')} title={intl.formatMessage(messages.favourite)} icon='star' onClick={this.handleFavouriteClick} counter={status.get('favourites_count')} />
  111. </div>
  112. );
  113. }
  114. }