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.

133 lines
4.4 KiB

  1. import React from 'react';
  2. import ImmutablePropTypes from 'react-immutable-proptypes';
  3. import PropTypes from 'prop-types';
  4. import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
  5. import IconButton from '../../../components/icon_button';
  6. import Motion from '../../ui/util/optional_motion';
  7. import spring from 'react-motion/lib/spring';
  8. import ImmutablePureComponent from 'react-immutable-pure-component';
  9. import { autoPlayGif, me } from '../../../initial_state';
  10. import classNames from 'classnames';
  11. const messages = defineMessages({
  12. unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' },
  13. follow: { id: 'account.follow', defaultMessage: 'Follow' },
  14. requested: { id: 'account.requested', defaultMessage: 'Awaiting approval. Click to cancel follow request' },
  15. });
  16. class Avatar extends ImmutablePureComponent {
  17. static propTypes = {
  18. account: ImmutablePropTypes.map.isRequired,
  19. };
  20. state = {
  21. isHovered: false,
  22. };
  23. handleMouseOver = () => {
  24. if (this.state.isHovered) return;
  25. this.setState({ isHovered: true });
  26. }
  27. handleMouseOut = () => {
  28. if (!this.state.isHovered) return;
  29. this.setState({ isHovered: false });
  30. }
  31. render () {
  32. const { account } = this.props;
  33. const { isHovered } = this.state;
  34. return (
  35. <Motion defaultStyle={{ radius: 90 }} style={{ radius: spring(isHovered ? 30 : 90, { stiffness: 180, damping: 12 }) }}>
  36. {({ radius }) => (
  37. <a
  38. href={account.get('url')}
  39. className='account__header__avatar'
  40. role='presentation'
  41. target='_blank'
  42. rel='noopener'
  43. style={{ borderRadius: `${radius}px`, backgroundImage: `url(${autoPlayGif || isHovered ? account.get('avatar') : account.get('avatar_static')})` }}
  44. onMouseOver={this.handleMouseOver}
  45. onMouseOut={this.handleMouseOut}
  46. onFocus={this.handleMouseOver}
  47. onBlur={this.handleMouseOut}
  48. >
  49. <span style={{ display: 'none' }}>{account.get('acct')}</span>
  50. </a>
  51. )}
  52. </Motion>
  53. );
  54. }
  55. }
  56. @injectIntl
  57. export default class Header extends ImmutablePureComponent {
  58. static propTypes = {
  59. account: ImmutablePropTypes.map,
  60. onFollow: PropTypes.func.isRequired,
  61. intl: PropTypes.object.isRequired,
  62. };
  63. render () {
  64. const { account, intl } = this.props;
  65. if (!account) {
  66. return null;
  67. }
  68. let info = '';
  69. let actionBtn = '';
  70. let lockedIcon = '';
  71. if (me !== account.get('id') && account.getIn(['relationship', 'followed_by'])) {
  72. info = <span className='account--follows-info'><FormattedMessage id='account.follows_you' defaultMessage='Follows you' /></span>;
  73. }
  74. if (me !== account.get('id')) {
  75. if (account.getIn(['relationship', 'requested'])) {
  76. actionBtn = (
  77. <div className='account--action-button'>
  78. <IconButton size={26} active icon='hourglass' title={intl.formatMessage(messages.requested)} onClick={this.props.onFollow} />
  79. </div>
  80. );
  81. } else if (!account.getIn(['relationship', 'blocking'])) {
  82. actionBtn = (
  83. <div className='account--action-button'>
  84. <IconButton size={26} icon={account.getIn(['relationship', 'following']) ? 'user-times' : 'user-plus'} active={account.getIn(['relationship', 'following'])} title={intl.formatMessage(account.getIn(['relationship', 'following']) ? messages.unfollow : messages.follow)} onClick={this.props.onFollow} />
  85. </div>
  86. );
  87. }
  88. }
  89. if (account.get('moved') && !account.getIn(['relationship', 'following'])) {
  90. actionBtn = '';
  91. }
  92. if (account.get('locked')) {
  93. lockedIcon = <i className='fa fa-lock' />;
  94. }
  95. const content = { __html: account.get('note_emojified') };
  96. const displayNameHtml = { __html: account.get('display_name_html') };
  97. return (
  98. <div className={classNames('account__header', { inactive: !!account.get('moved') })} style={{ backgroundImage: `url(${account.get('header')})` }}>
  99. <div>
  100. <Avatar account={account} />
  101. <span className='account__header__display-name' dangerouslySetInnerHTML={displayNameHtml} />
  102. <span className='account__header__username'>@{account.get('acct')} {lockedIcon}</span>
  103. <div className='account__header__content' dangerouslySetInnerHTML={content} />
  104. {info}
  105. {actionBtn}
  106. </div>
  107. </div>
  108. );
  109. }
  110. }