Show boosted user's avatar (#2518)

* Show boosted user's avatar

* add .status__avatar-boost

* margin

* apply to notifications too.

* account__avatar-boost

* Add inline prop to Avatar component

* Add AvatarOverlay component

* rename mixins.scss

* move files for latest master

* fixed for webpack
This commit is contained in:
kawax 2017-05-03 18:43:37 +09:00 committed by Eugen Rochko
parent bf8031e984
commit 383c0b7802
7 changed files with 97 additions and 15 deletions

View File

@ -25,9 +25,15 @@ class Avatar extends React.PureComponent {
} }
render () { render () {
const { src, size, staticSrc, animate } = this.props; const { src, size, staticSrc, animate, inline } = this.props;
const { hovering } = this.state; const { hovering } = this.state;
let className = 'account__avatar';
if (inline) {
className = className + ' account__avatar-inline';
}
const style = { const style = {
...this.props.style, ...this.props.style,
width: `${size}px`, width: `${size}px`,
@ -43,7 +49,7 @@ class Avatar extends React.PureComponent {
return ( return (
<div <div
className='account__avatar' className={className}
onMouseEnter={this.handleMouseEnter} onMouseEnter={this.handleMouseEnter}
onMouseLeave={this.handleMouseLeave} onMouseLeave={this.handleMouseLeave}
style={style} style={style}
@ -58,11 +64,14 @@ Avatar.propTypes = {
staticSrc: PropTypes.string, staticSrc: PropTypes.string,
size: PropTypes.number.isRequired, size: PropTypes.number.isRequired,
style: PropTypes.object, style: PropTypes.object,
animate: PropTypes.bool animate: PropTypes.bool,
inline: PropTypes.bool
}; };
Avatar.defaultProps = { Avatar.defaultProps = {
animate: false animate: false,
size: 20,
inline: false
}; };
export default Avatar; export default Avatar;

View File

@ -0,0 +1,30 @@
import React from 'react';
import PropTypes from 'prop-types';
class AvatarOverlay extends React.PureComponent {
render() {
const {staticSrc, overlaySrc} = this.props;
const baseStyle = {
backgroundImage: `url(${staticSrc})`
};
const overlayStyle = {
backgroundImage: `url(${overlaySrc})`
};
return (
<div className='account__avatar-overlay'>
<div className="account__avatar-overlay-base" style={baseStyle} />
<div className="account__avatar-overlay-overlay" style={overlayStyle} />
</div>
);
}
}
AvatarOverlay.propTypes = {
staticSrc: PropTypes.string.isRequired,
overlaySrc: PropTypes.string.isRequired,
};
export default AvatarOverlay;

View File

@ -2,6 +2,7 @@ import React from 'react';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import Avatar from './avatar'; import Avatar from './avatar';
import AvatarOverlay from './avatar_overlay';
import RelativeTimestamp from './relative_timestamp'; import RelativeTimestamp from './relative_timestamp';
import DisplayName from './display_name'; import DisplayName from './display_name';
import MediaGallery from './media_gallery'; import MediaGallery from './media_gallery';
@ -36,7 +37,8 @@ class Status extends ImmutablePureComponent {
render () { render () {
let media = ''; let media = '';
const { status, ...other } = this.props; let statusAvatar;
const { status, account, ...other } = this.props;
if (status === null) { if (status === null) {
return <div />; return <div />;
@ -58,7 +60,7 @@ class Status extends ImmutablePureComponent {
<FormattedMessage id='status.reblogged_by' defaultMessage='{name} boosted' values={{ name: <a onClick={this.handleAccountClick.bind(this, status.getIn(['account', 'id']))} href={status.getIn(['account', 'url'])} className='status__display-name muted'><strong dangerouslySetInnerHTML={displayNameHTML} /></a> }} /> <FormattedMessage id='status.reblogged_by' defaultMessage='{name} boosted' values={{ name: <a onClick={this.handleAccountClick.bind(this, status.getIn(['account', 'id']))} href={status.getIn(['account', 'url'])} className='status__display-name muted'><strong dangerouslySetInnerHTML={displayNameHTML} /></a> }} />
</div> </div>
<Status {...other} wrapped={true} status={status.get('reblog')} /> <Status {...other} wrapped={true} status={status.get('reblog')} account={status.get('account')} />
</div> </div>
); );
} }
@ -73,6 +75,12 @@ class Status extends ImmutablePureComponent {
} }
} }
if (account === undefined || account === null) {
statusAvatar = <Avatar src={status.getIn(['account', 'avatar'])} staticSrc={status.getIn(['account', 'avatar_static'])} size={48}/>;
}else{
statusAvatar = <AvatarOverlay staticSrc={status.getIn(['account', 'avatar_static'])} overlaySrc={account.get('avatar_static')} />;
}
return ( return (
<div className={this.props.muted ? 'status muted' : 'status'}> <div className={this.props.muted ? 'status muted' : 'status'}>
<div className='status__info'> <div className='status__info'>
@ -82,7 +90,7 @@ class Status extends ImmutablePureComponent {
<a onClick={this.handleAccountClick.bind(this, status.getIn(['account', 'id']))} href={status.getIn(['account', 'url'])} className='status__display-name'> <a onClick={this.handleAccountClick.bind(this, status.getIn(['account', 'id']))} 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} /> {statusAvatar}
</div> </div>
<DisplayName account={status.get('account')} /> <DisplayName account={status.get('account')} />
@ -106,6 +114,7 @@ Status.contextTypes = {
Status.propTypes = { Status.propTypes = {
status: ImmutablePropTypes.map, status: ImmutablePropTypes.map,
account: ImmutablePropTypes.map,
wrapped: PropTypes.bool, wrapped: PropTypes.bool,
onReply: PropTypes.func, onReply: PropTypes.func,
onFavourite: PropTypes.func, onFavourite: PropTypes.func,

View File

@ -2,6 +2,7 @@ import React from 'react';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import StatusContainer from '../../../containers/status_container'; import StatusContainer from '../../../containers/status_container';
import AccountContainer from '../../../containers/account_container'; import AccountContainer from '../../../containers/account_container';
import Avatar from '../../../components/avatar';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import Permalink from '../../../components/permalink'; import Permalink from '../../../components/permalink';
import emojify from '../../../emoji'; import emojify from '../../../emoji';
@ -37,11 +38,10 @@ class Notification extends ImmutablePureComponent {
<div className='notification__favourite-icon-wrapper'> <div className='notification__favourite-icon-wrapper'>
<i className='fa fa-fw fa-star star-icon'/> <i className='fa fa-fw fa-star star-icon'/>
</div> </div>
<FormattedMessage id='notification.favourite' defaultMessage='{name} favourited your status' values={{ name: link }} /> <FormattedMessage id='notification.favourite' defaultMessage='{name} favourited your status' values={{ name: link }} />
</div> </div>
<StatusContainer id={notification.get('status')} muted={true} /> <StatusContainer id={notification.get('status')} account={notification.get('account')} muted={true} />
</div> </div>
); );
} }
@ -53,11 +53,10 @@ class Notification extends ImmutablePureComponent {
<div className='notification__favourite-icon-wrapper'> <div className='notification__favourite-icon-wrapper'>
<i className='fa fa-fw fa-retweet' /> <i className='fa fa-fw fa-retweet' />
</div> </div>
<FormattedMessage id='notification.reblog' defaultMessage='{name} boosted your status' values={{ name: link }} /> <FormattedMessage id='notification.reblog' defaultMessage='{name} boosted your status' values={{ name: link }} />
</div> </div>
<StatusContainer id={notification.get('status')} muted={true} /> <StatusContainer id={notification.get('status')} account={notification.get('account')} muted={true} />
</div> </div>
); );
} }

View File

@ -0,0 +1,12 @@
@mixin avatar-radius() {
border-radius: 4px;
background: transparent no-repeat;
background-position: 50%;
background-clip: padding-box;
}
@mixin avatar-size($size:48px) {
width: $size;
height: $size;
background-size: $size $size;
}

View File

@ -1,3 +1,4 @@
@import 'mixins';
@import 'variables'; @import 'variables';
@import 'fonts/roboto'; @import 'fonts/roboto';
@import 'fonts/roboto-mono'; @import 'fonts/roboto-mono';

View File

@ -673,11 +673,33 @@ a.status__content__spoiler-link {
} }
.account__avatar { .account__avatar {
border-radius: 4px; @include avatar-radius();
background: transparent no-repeat;
background-position: 50%;
background-clip: padding-box;
position: relative; position: relative;
&-inline {
display: inline-block;
vertical-align: middle;
margin-right: 5px;
}
}
.account__avatar-overlay {
@include avatar-size(48px);
&-base {
@include avatar-radius();
@include avatar-size(36px);
}
&-overlay {
@include avatar-radius();
@include avatar-size(24px);
position: absolute;
bottom: 0;
right: 0;
z-index: 1;
}
} }
.account__relationship { .account__relationship {