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.

247 lines
8.9 KiB

  1. import React from 'react';
  2. import ImmutablePureComponent from 'react-immutable-pure-component';
  3. import ImmutablePropTypes from 'react-immutable-proptypes';
  4. import PropTypes from 'prop-types';
  5. import { connect } from 'react-redux';
  6. import { makeGetAccount } from 'flavours/glitch/selectors';
  7. import Avatar from 'flavours/glitch/components/avatar';
  8. import DisplayName from 'flavours/glitch/components/display_name';
  9. import Permalink from 'flavours/glitch/components/permalink';
  10. import IconButton from 'flavours/glitch/components/icon_button';
  11. import Button from 'flavours/glitch/components/button';
  12. import { FormattedMessage, injectIntl, defineMessages } from 'react-intl';
  13. import { autoPlayGif, me, unfollowModal } from 'flavours/glitch/initial_state';
  14. import ShortNumber from 'flavours/glitch/components/short_number';
  15. import {
  16. followAccount,
  17. unfollowAccount,
  18. unblockAccount,
  19. unmuteAccount,
  20. } from 'flavours/glitch/actions/accounts';
  21. import { openModal } from 'flavours/glitch/actions/modal';
  22. import classNames from 'classnames';
  23. const messages = defineMessages({
  24. unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' },
  25. follow: { id: 'account.follow', defaultMessage: 'Follow' },
  26. cancel_follow_request: { id: 'account.cancel_follow_request', defaultMessage: 'Withdraw follow request' },
  27. cancelFollowRequestConfirm: { id: 'confirmations.cancel_follow_request.confirm', defaultMessage: 'Withdraw request' },
  28. requested: { id: 'account.requested', defaultMessage: 'Awaiting approval. Click to cancel follow request' },
  29. unblock: { id: 'account.unblock_short', defaultMessage: 'Unblock' },
  30. unmute: { id: 'account.unmute_short', defaultMessage: 'Unmute' },
  31. unfollowConfirm: { id: 'confirmations.unfollow.confirm', defaultMessage: 'Unfollow' },
  32. edit_profile: { id: 'account.edit_profile', defaultMessage: 'Edit profile' },
  33. dismissSuggestion: { id: 'suggestions.dismiss', defaultMessage: 'Dismiss suggestion' },
  34. });
  35. const makeMapStateToProps = () => {
  36. const getAccount = makeGetAccount();
  37. const mapStateToProps = (state, { id }) => ({
  38. account: getAccount(state, id),
  39. });
  40. return mapStateToProps;
  41. };
  42. const mapDispatchToProps = (dispatch, { intl }) => ({
  43. onFollow(account) {
  44. if (account.getIn(['relationship', 'following'])) {
  45. if (unfollowModal) {
  46. dispatch(
  47. openModal('CONFIRM', {
  48. message: (
  49. <FormattedMessage
  50. id='confirmations.unfollow.message'
  51. defaultMessage='Are you sure you want to unfollow {name}?'
  52. values={{ name: <strong>@{account.get('acct')}</strong> }}
  53. />
  54. ),
  55. confirm: intl.formatMessage(messages.unfollowConfirm),
  56. onConfirm: () => dispatch(unfollowAccount(account.get('id'))),
  57. }),
  58. );
  59. } else {
  60. dispatch(unfollowAccount(account.get('id')));
  61. }
  62. } else if (account.getIn(['relationship', 'requested'])) {
  63. if (unfollowModal) {
  64. dispatch(openModal('CONFIRM', {
  65. message: <FormattedMessage id='confirmations.cancel_follow_request.message' defaultMessage='Are you sure you want to withdraw your request to follow {name}?' values={{ name: <strong>@{account.get('acct')}</strong> }} />,
  66. confirm: intl.formatMessage(messages.cancelFollowRequestConfirm),
  67. onConfirm: () => dispatch(unfollowAccount(account.get('id'))),
  68. }));
  69. } else {
  70. dispatch(unfollowAccount(account.get('id')));
  71. }
  72. } else {
  73. dispatch(followAccount(account.get('id')));
  74. }
  75. },
  76. onBlock(account) {
  77. if (account.getIn(['relationship', 'blocking'])) {
  78. dispatch(unblockAccount(account.get('id')));
  79. }
  80. },
  81. onMute(account) {
  82. if (account.getIn(['relationship', 'muting'])) {
  83. dispatch(unmuteAccount(account.get('id')));
  84. }
  85. },
  86. });
  87. class AccountCard extends ImmutablePureComponent {
  88. static propTypes = {
  89. account: ImmutablePropTypes.map.isRequired,
  90. intl: PropTypes.object.isRequired,
  91. onFollow: PropTypes.func.isRequired,
  92. onBlock: PropTypes.func.isRequired,
  93. onMute: PropTypes.func.isRequired,
  94. onDismiss: PropTypes.func,
  95. };
  96. handleMouseEnter = ({ currentTarget }) => {
  97. if (autoPlayGif) {
  98. return;
  99. }
  100. const emojis = currentTarget.querySelectorAll('.custom-emoji');
  101. for (var i = 0; i < emojis.length; i++) {
  102. let emoji = emojis[i];
  103. emoji.src = emoji.getAttribute('data-original');
  104. }
  105. };
  106. handleMouseLeave = ({ currentTarget }) => {
  107. if (autoPlayGif) {
  108. return;
  109. }
  110. const emojis = currentTarget.querySelectorAll('.custom-emoji');
  111. for (var i = 0; i < emojis.length; i++) {
  112. let emoji = emojis[i];
  113. emoji.src = emoji.getAttribute('data-static');
  114. }
  115. };
  116. handleFollow = () => {
  117. this.props.onFollow(this.props.account);
  118. };
  119. handleBlock = () => {
  120. this.props.onBlock(this.props.account);
  121. };
  122. handleMute = () => {
  123. this.props.onMute(this.props.account);
  124. };
  125. handleEditProfile = () => {
  126. window.open('/settings/profile', '_blank');
  127. };
  128. handleDismiss = (e) => {
  129. const { account, onDismiss } = this.props;
  130. onDismiss(account.get('id'));
  131. e.preventDefault();
  132. e.stopPropagation();
  133. };
  134. render() {
  135. const { account, intl } = this.props;
  136. let actionBtn;
  137. if (me !== account.get('id')) {
  138. if (!account.get('relationship')) { // Wait until the relationship is loaded
  139. actionBtn = '';
  140. } else if (account.getIn(['relationship', 'requested'])) {
  141. actionBtn = <Button className={classNames('logo-button')} text={intl.formatMessage(messages.cancel_follow_request)} title={intl.formatMessage(messages.requested)} onClick={this.handleFollow} />;
  142. } else if (account.getIn(['relationship', 'muting'])) {
  143. actionBtn = <Button className='logo-button' text={intl.formatMessage(messages.unmute)} onClick={this.handleMute} />;
  144. } else if (!account.getIn(['relationship', 'blocking'])) {
  145. actionBtn = <Button disabled={account.getIn(['relationship', 'blocked_by'])} className={classNames('logo-button', { 'button--destructive': account.getIn(['relationship', 'following']) })} text={intl.formatMessage(account.getIn(['relationship', 'following']) ? messages.unfollow : messages.follow)} onClick={this.handleFollow} />;
  146. } else if (account.getIn(['relationship', 'blocking'])) {
  147. actionBtn = <Button className='logo-button' text={intl.formatMessage(messages.unblock)} onClick={this.handleBlock} />;
  148. }
  149. } else {
  150. actionBtn = <Button className='logo-button' text={intl.formatMessage(messages.edit_profile)} onClick={this.handleEditProfile} />;
  151. }
  152. return (
  153. <div className='account-card'>
  154. <Permalink href={account.get('url')} to={`/@${account.get('acct')}`} className='account-card__permalink'>
  155. <div className='account-card__header'>
  156. {this.props.onDismiss && <IconButton className='media-modal__close' title={intl.formatMessage(messages.dismissSuggestion)} icon='times' onClick={this.handleDismiss} size={20} />}
  157. <img
  158. src={
  159. autoPlayGif ? account.get('header') : account.get('header_static')
  160. }
  161. alt=''
  162. />
  163. </div>
  164. <div className='account-card__title'>
  165. <div className='account-card__title__avatar'><Avatar account={account} size={56} /></div>
  166. <DisplayName account={account} />
  167. </div>
  168. </Permalink>
  169. {account.get('note').length > 0 && (
  170. <div
  171. className='account-card__bio translate'
  172. onMouseEnter={this.handleMouseEnter}
  173. onMouseLeave={this.handleMouseLeave}
  174. dangerouslySetInnerHTML={{ __html: account.get('note_emojified') }}
  175. />
  176. )}
  177. <div className='account-card__actions'>
  178. <div className='account-card__counters'>
  179. <div className='account-card__counters__item'>
  180. <ShortNumber value={account.get('statuses_count')} />
  181. <small>
  182. <FormattedMessage id='account.posts' defaultMessage='Posts' />
  183. </small>
  184. </div>
  185. <div className='account-card__counters__item'>
  186. {account.get('followers_count') < 0 ? '-' : <ShortNumber value={account.get('followers_count')} />}{' '}
  187. <small>
  188. <FormattedMessage
  189. id='account.followers'
  190. defaultMessage='Followers'
  191. />
  192. </small>
  193. </div>
  194. <div className='account-card__counters__item'>
  195. <ShortNumber value={account.get('following_count')} />{' '}
  196. <small>
  197. <FormattedMessage
  198. id='account.following'
  199. defaultMessage='Following'
  200. />
  201. </small>
  202. </div>
  203. </div>
  204. <div className='account-card__actions__button'>
  205. {actionBtn}
  206. </div>
  207. </div>
  208. </div>
  209. );
  210. }
  211. }
  212. export default injectIntl(connect(makeMapStateToProps, mapDispatchToProps)(AccountCard));