Merged upstream PR #4526
This commit is contained in:
		@@ -70,7 +70,7 @@ export default class Account extends ImmutablePureComponent {
 | 
				
			|||||||
      <div className='account'>
 | 
					      <div className='account'>
 | 
				
			||||||
        <div className='account__wrapper'>
 | 
					        <div className='account__wrapper'>
 | 
				
			||||||
          <Permalink key={account.get('id')} className='account__display-name' href={account.get('url')} to={`/accounts/${account.get('id')}`}>
 | 
					          <Permalink key={account.get('id')} className='account__display-name' href={account.get('url')} to={`/accounts/${account.get('id')}`}>
 | 
				
			||||||
            <div className='account__avatar-wrapper'><Avatar src={account.get('avatar')} staticSrc={account.get('avatar_static')} size={36} /></div>
 | 
					            <div className='account__avatar-wrapper'><Avatar account={account} size={36} /></div>
 | 
				
			||||||
            <DisplayName account={account} />
 | 
					            <DisplayName account={account} />
 | 
				
			||||||
          </Permalink>
 | 
					          </Permalink>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,11 +1,11 @@
 | 
				
			|||||||
import React from 'react';
 | 
					import React from 'react';
 | 
				
			||||||
import PropTypes from 'prop-types';
 | 
					import PropTypes from 'prop-types';
 | 
				
			||||||
 | 
					import ImmutablePropTypes from 'react-immutable-proptypes';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default class Avatar extends React.PureComponent {
 | 
					export default class Avatar extends React.PureComponent {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  static propTypes = {
 | 
					  static propTypes = {
 | 
				
			||||||
    src: PropTypes.string.isRequired,
 | 
					    account: ImmutablePropTypes.map.isRequired,
 | 
				
			||||||
    staticSrc: PropTypes.string,
 | 
					 | 
				
			||||||
    size: PropTypes.number.isRequired,
 | 
					    size: PropTypes.number.isRequired,
 | 
				
			||||||
    style: PropTypes.object,
 | 
					    style: PropTypes.object,
 | 
				
			||||||
    animate: PropTypes.bool,
 | 
					    animate: PropTypes.bool,
 | 
				
			||||||
@@ -33,9 +33,12 @@ export default class Avatar extends React.PureComponent {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  render () {
 | 
					  render () {
 | 
				
			||||||
    const { src, size, staticSrc, animate, inline } = this.props;
 | 
					    const { account, size, animate, inline } = this.props;
 | 
				
			||||||
    const { hovering } = this.state;
 | 
					    const { hovering } = this.state;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const src = account.get('avatar');
 | 
				
			||||||
 | 
					    const staticSrc = account.get('avatar_static');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let className = 'account__avatar';
 | 
					    let className = 'account__avatar';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (inline) {
 | 
					    if (inline) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,22 +1,22 @@
 | 
				
			|||||||
import React from 'react';
 | 
					import React from 'react';
 | 
				
			||||||
import PropTypes from 'prop-types';
 | 
					import ImmutablePropTypes from 'react-immutable-proptypes';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default class AvatarOverlay extends React.PureComponent {
 | 
					export default class AvatarOverlay extends React.PureComponent {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  static propTypes = {
 | 
					  static propTypes = {
 | 
				
			||||||
    staticSrc: PropTypes.string.isRequired,
 | 
					    account: ImmutablePropTypes.map.isRequired,
 | 
				
			||||||
    overlaySrc: PropTypes.string.isRequired,
 | 
					    friend: ImmutablePropTypes.map.isRequired,
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  render() {
 | 
					  render() {
 | 
				
			||||||
    const { staticSrc, overlaySrc } = this.props;
 | 
					    const { account, friend } = this.props;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const baseStyle = {
 | 
					    const baseStyle = {
 | 
				
			||||||
      backgroundImage: `url(${staticSrc})`,
 | 
					      backgroundImage: `url(${account.get('avatar_static')})`,
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const overlayStyle = {
 | 
					    const overlayStyle = {
 | 
				
			||||||
      backgroundImage: `url(${overlaySrc})`,
 | 
					      backgroundImage: `url(${friend.get('avatar_static')})`,
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -231,9 +231,9 @@ export default class Status extends ImmutablePureComponent {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (account === undefined || account === null) {
 | 
					    if (account === undefined || account === null) {
 | 
				
			||||||
      statusAvatar = <Avatar src={status.getIn(['account', 'avatar'])} staticSrc={status.getIn(['account', 'avatar_static'])} size={48} />;
 | 
					      statusAvatar = <Avatar account={status.get('account')} size={48} />;
 | 
				
			||||||
    }else{
 | 
					    }else{
 | 
				
			||||||
      statusAvatar = <AvatarOverlay staticSrc={status.getIn(['account', 'avatar_static'])} overlaySrc={account.get('avatar_static')} />;
 | 
					      statusAvatar = <AvatarOverlay account={status.get('account')} friend={account} />;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,7 +15,7 @@ export default class AutosuggestAccount extends ImmutablePureComponent {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
      <div className='autosuggest-account'>
 | 
					      <div className='autosuggest-account'>
 | 
				
			||||||
        <div className='autosuggest-account-icon'><Avatar src={account.get('avatar')} staticSrc={account.get('avatar_static')} size={18} /></div>
 | 
					        <div className='autosuggest-account-icon'><Avatar account={account} size={18} /></div>
 | 
				
			||||||
        <DisplayName account={account} />
 | 
					        <DisplayName account={account} />
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,7 +19,7 @@ export default class NavigationBar extends ImmutablePureComponent {
 | 
				
			|||||||
      <div className='navigation-bar'>
 | 
					      <div className='navigation-bar'>
 | 
				
			||||||
        <Permalink href={this.props.account.get('url')} to={`/accounts/${this.props.account.get('id')}`}>
 | 
					        <Permalink href={this.props.account.get('url')} to={`/accounts/${this.props.account.get('id')}`}>
 | 
				
			||||||
          <span style={{ display: 'none' }}>{this.props.account.get('acct')}</span>
 | 
					          <span style={{ display: 'none' }}>{this.props.account.get('acct')}</span>
 | 
				
			||||||
          <Avatar src={this.props.account.get('avatar')} staticSrc={this.props.account.get('avatar_static')} size={40} />
 | 
					          <Avatar account={this.props.account} size={40} />
 | 
				
			||||||
        </Permalink>
 | 
					        </Permalink>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <div className='navigation-bar__profile'>
 | 
					        <div className='navigation-bar__profile'>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -51,7 +51,7 @@ export default class ReplyIndicator extends ImmutablePureComponent {
 | 
				
			|||||||
          <div className='reply-indicator__cancel'><IconButton title={intl.formatMessage(messages.cancel)} icon='times' onClick={this.handleClick} /></div>
 | 
					          <div className='reply-indicator__cancel'><IconButton title={intl.formatMessage(messages.cancel)} icon='times' onClick={this.handleClick} /></div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          <a href={status.getIn(['account', 'url'])} onClick={this.handleAccountClick} className='reply-indicator__display-name'>
 | 
					          <a href={status.getIn(['account', 'url'])} onClick={this.handleAccountClick} className='reply-indicator__display-name'>
 | 
				
			||||||
            <div className='reply-indicator__display-avatar'><Avatar size={24} src={status.getIn(['account', 'avatar'])} staticSrc={status.getIn(['account', 'avatar_static'])} /></div>
 | 
					            <div className='reply-indicator__display-avatar'><Avatar account={status.get('account')} size={24} /></div>
 | 
				
			||||||
            <DisplayName account={status.get('account')} />
 | 
					            <DisplayName account={status.get('account')} />
 | 
				
			||||||
          </a>
 | 
					          </a>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -32,7 +32,7 @@ export default class AccountAuthorize extends ImmutablePureComponent {
 | 
				
			|||||||
      <div className='account-authorize__wrapper'>
 | 
					      <div className='account-authorize__wrapper'>
 | 
				
			||||||
        <div className='account-authorize'>
 | 
					        <div className='account-authorize'>
 | 
				
			||||||
          <Permalink href={account.get('url')} to={`/accounts/${account.get('id')}`} className='detailed-status__display-name'>
 | 
					          <Permalink href={account.get('url')} to={`/accounts/${account.get('id')}`} className='detailed-status__display-name'>
 | 
				
			||||||
            <div className='account-authorize__avatar'><Avatar src={account.get('avatar')} staticSrc={account.get('avatar_static')} size={48} /></div>
 | 
					            <div className='account-authorize__avatar'><Avatar account={account} size={48} /></div>
 | 
				
			||||||
            <DisplayName account={account} />
 | 
					            <DisplayName account={account} />
 | 
				
			||||||
          </Permalink>
 | 
					          </Permalink>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -81,7 +81,7 @@ export default class DetailedStatus extends ImmutablePureComponent {
 | 
				
			|||||||
    return (
 | 
					    return (
 | 
				
			||||||
      <div className='detailed-status'>
 | 
					      <div className='detailed-status'>
 | 
				
			||||||
        <a href={status.getIn(['account', 'url'])} onClick={this.handleAccountClick} className='detailed-status__display-name'>
 | 
					        <a href={status.getIn(['account', 'url'])} onClick={this.handleAccountClick} className='detailed-status__display-name'>
 | 
				
			||||||
          <div className='detailed-status__display-avatar'><Avatar src={status.getIn(['account', 'avatar'])} staticSrc={status.getIn(['account', 'avatar_static'])} size={48} /></div>
 | 
					          <div className='detailed-status__display-avatar'><Avatar account={status.get('account')} size={48} /></div>
 | 
				
			||||||
          <DisplayName account={status.get('account')} />
 | 
					          <DisplayName account={status.get('account')} />
 | 
				
			||||||
        </a>
 | 
					        </a>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -46,7 +46,7 @@ export default class ActionsModal extends ImmutablePureComponent {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
          <a href={this.props.status.getIn(['account', 'url'])} className='status__display-name'>
 | 
					          <a href={this.props.status.getIn(['account', 'url'])} className='status__display-name'>
 | 
				
			||||||
            <div className='status__avatar'>
 | 
					            <div className='status__avatar'>
 | 
				
			||||||
              <Avatar src={this.props.status.getIn(['account', 'avatar'])} staticSrc={this.props.status.getIn(['account', 'avatar_static'])} size={48} />
 | 
					              <Avatar account={this.props.status.get('account')} size={48} />
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <DisplayName account={this.props.status.get('account')} />
 | 
					            <DisplayName account={this.props.status.get('account')} />
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -62,7 +62,7 @@ export default class BoostModal extends ImmutablePureComponent {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
              <a onClick={this.handleAccountClick} href={status.getIn(['account', 'url'])} className='status__display-name'>
 | 
					              <a onClick={this.handleAccountClick} href={status.getIn(['account', 'url'])} className='status__display-name'>
 | 
				
			||||||
                <div className='status__avatar'>
 | 
					                <div className='status__avatar'>
 | 
				
			||||||
                  <Avatar src={status.getIn(['account', 'avatar'])} staticSrc={status.getIn(['account', 'avatar_static'])} size={48} />
 | 
					                  <Avatar account={status.get('account')} size={48} />
 | 
				
			||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                <DisplayName account={status.get('account')} />
 | 
					                <DisplayName account={status.get('account')} />
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,20 +1,42 @@
 | 
				
			|||||||
import { expect } from 'chai';
 | 
					import { expect } from 'chai';
 | 
				
			||||||
import { render } from 'enzyme';
 | 
					import { render } from 'enzyme';
 | 
				
			||||||
 | 
					import { fromJS }  from 'immutable';
 | 
				
			||||||
import React from 'react';
 | 
					import React from 'react';
 | 
				
			||||||
import Avatar from '../../../app/javascript/mastodon/components/avatar';
 | 
					import Avatar from '../../../app/javascript/mastodon/components/avatar';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('<Avatar />', () => {
 | 
					describe('<Avatar />', () => {
 | 
				
			||||||
  const src = '/path/to/image.jpg';
 | 
					  const account = fromJS({
 | 
				
			||||||
 | 
					    username: 'alice',
 | 
				
			||||||
 | 
					    acct: 'alice',
 | 
				
			||||||
 | 
					    display_name: 'Alice',
 | 
				
			||||||
 | 
					    avatar: '/animated/alice.gif',
 | 
				
			||||||
 | 
					    avatar_static: '/static/alice.jpg',
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
  const size = 100;
 | 
					  const size = 100;
 | 
				
			||||||
  const wrapper = render(<Avatar src={src} animate size={size} />);
 | 
					  const animated = render(<Avatar account={account} animate size={size} />);
 | 
				
			||||||
 | 
					  const still = render(<Avatar account={account} size={size} />);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Autoplay
 | 
				
			||||||
  it('renders a div element with the given src as background', () => {
 | 
					  it('renders a div element with the given src as background', () => {
 | 
				
			||||||
    expect(wrapper.find('div')).to.have.style('background-image', `url(${src})`);
 | 
					    expect(animated.find('div')).to.have.style('background-image', `url(${account.get('avatar')})`);
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  it('renders a div element of the given size', () => {
 | 
					  it('renders a div element of the given size', () => {
 | 
				
			||||||
    ['width', 'height'].map((attr) => {
 | 
					    ['width', 'height'].map((attr) => {
 | 
				
			||||||
      expect(wrapper.find('div')).to.have.style(attr, `${size}px`);
 | 
					      expect(animated.find('div')).to.have.style(attr, `${size}px`);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Still
 | 
				
			||||||
 | 
					  it('renders a div element with the given static src as background if not autoplay', () => {
 | 
				
			||||||
 | 
					    expect(still.find('div')).to.have.style('background-image', `url(${account.get('avatar_static')})`);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  it('renders a div element of the given size if not autoplay', () => {
 | 
				
			||||||
 | 
					    ['width', 'height'].map((attr) => {
 | 
				
			||||||
 | 
					      expect(still.find('div')).to.have.style(attr, `${size}px`);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // TODO add autoplay test if possible
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										34
									
								
								spec/javascript/components/avatar_overlay.test.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								spec/javascript/components/avatar_overlay.test.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,34 @@
 | 
				
			|||||||
 | 
					import { expect } from 'chai';
 | 
				
			||||||
 | 
					import { render } from 'enzyme';
 | 
				
			||||||
 | 
					import { fromJS }  from 'immutable';
 | 
				
			||||||
 | 
					import React from 'react';
 | 
				
			||||||
 | 
					import AvatarOverlay from '../../../app/javascript/mastodon/components/avatar_overlay';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					describe('<Avatar />', () => {
 | 
				
			||||||
 | 
					  const account = fromJS({
 | 
				
			||||||
 | 
					    username: 'alice',
 | 
				
			||||||
 | 
					    acct: 'alice',
 | 
				
			||||||
 | 
					    display_name: 'Alice',
 | 
				
			||||||
 | 
					    avatar: '/animated/alice.gif',
 | 
				
			||||||
 | 
					    avatar_static: '/static/alice.jpg',
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  const friend = fromJS({
 | 
				
			||||||
 | 
					    username: 'eve',
 | 
				
			||||||
 | 
					    acct: 'eve@blackhat.lair',
 | 
				
			||||||
 | 
					    display_name: 'Evelyn',
 | 
				
			||||||
 | 
					    avatar: '/animated/eve.gif',
 | 
				
			||||||
 | 
					    avatar_static: '/static/eve.jpg',
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const overlay = render(<AvatarOverlay account={account} friend={friend} />);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  it('renders account static src as base of overlay avatar', () => {
 | 
				
			||||||
 | 
					    expect(overlay.find('.account__avatar-overlay-base'))
 | 
				
			||||||
 | 
					      .to.have.style('background-image', `url(${account.get('avatar_static')})`);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  it('renders friend static src as overlay of overlay avatar', () => {
 | 
				
			||||||
 | 
					    expect(overlay.find('.account__avatar-overlay-overlay'))
 | 
				
			||||||
 | 
					      .to.have.style('background-image', `url(${friend.get('avatar_static')})`);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
		Reference in New Issue
	
	Block a user