[Glitch] Update `Avatar`, `AvatarComposite`, and `AvatarOverlay` components (#2508)
Various ports including8dfe5179ee
,d1de7fb7fa
and9f8d34620b
. Co-authored-by: Eugen Rochko <eugen@zeonfederated.com> Co-authored-by: fusagiko / takayamaki <24884114+takayamaki@users.noreply.github.com>
This commit is contained in:
parent
c0e562916c
commit
b2647bc3f2
|
@ -5,49 +5,44 @@ import { autoPlayGif } from '../initial_state';
|
||||||
import type { Account } from '../types/resources';
|
import type { Account } from '../types/resources';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
account: Account | undefined;
|
account: Account | undefined; // FIXME: remove `undefined` once we know for sure its always there
|
||||||
className?: string;
|
|
||||||
size: number;
|
size: number;
|
||||||
style?: React.CSSProperties;
|
style?: React.CSSProperties;
|
||||||
inline?: boolean;
|
inline?: boolean;
|
||||||
|
animate?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Avatar: React.FC<Props> = ({
|
export const Avatar: React.FC<Props> = ({
|
||||||
account,
|
account,
|
||||||
className,
|
animate = autoPlayGif,
|
||||||
size = 20,
|
size = 20,
|
||||||
inline = false,
|
inline = false,
|
||||||
style: styleFromParent,
|
style: styleFromParent,
|
||||||
}) => {
|
}) => {
|
||||||
const { hovering, handleMouseEnter, handleMouseLeave } =
|
const { hovering, handleMouseEnter, handleMouseLeave } = useHovering(animate);
|
||||||
useHovering(autoPlayGif);
|
|
||||||
|
|
||||||
const style = {
|
const style = {
|
||||||
...styleFromParent,
|
...styleFromParent,
|
||||||
width: `${size}px`,
|
width: `${size}px`,
|
||||||
height: `${size}px`,
|
height: `${size}px`,
|
||||||
backgroundSize: `${size}px ${size}px`,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (account) {
|
const src =
|
||||||
style.backgroundImage = `url(${account.get(
|
hovering || animate
|
||||||
hovering ? 'avatar' : 'avatar_static',
|
? account?.get('avatar')
|
||||||
)})`;
|
: account?.get('avatar_static');
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={classNames(
|
className={classNames('account__avatar', {
|
||||||
'account__avatar',
|
'account__avatar-inline': inline,
|
||||||
{ 'account__avatar-inline': inline },
|
})}
|
||||||
className,
|
|
||||||
)}
|
|
||||||
onMouseEnter={handleMouseEnter}
|
onMouseEnter={handleMouseEnter}
|
||||||
onMouseLeave={handleMouseLeave}
|
onMouseLeave={handleMouseLeave}
|
||||||
style={style}
|
style={style}
|
||||||
data-avatar-of={account && `@${account.get('acct')}`}
|
data-avatar-of={account && `@${account.get('acct')}`}
|
||||||
role='img'
|
>
|
||||||
aria-label={account?.get('acct')}
|
{src && <img src={src} alt={account?.get('acct')} />}
|
||||||
/>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,6 +5,8 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
|
|
||||||
import { autoPlayGif } from '../initial_state';
|
import { autoPlayGif } from '../initial_state';
|
||||||
|
|
||||||
|
import { Avatar } from './avatar';
|
||||||
|
|
||||||
export default class AvatarComposite extends PureComponent {
|
export default class AvatarComposite extends PureComponent {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
|
@ -76,12 +78,12 @@ export default class AvatarComposite extends PureComponent {
|
||||||
bottom: bottom,
|
bottom: bottom,
|
||||||
width: `${width}%`,
|
width: `${width}%`,
|
||||||
height: `${height}%`,
|
height: `${height}%`,
|
||||||
backgroundSize: 'cover',
|
|
||||||
backgroundImage: `url(${account.get(animate ? 'avatar' : 'avatar_static')})`,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={account.get('id')} style={style} data-avatar-of={`@${account.get('acct')}`} />
|
<div key={account.get('id')} style={style}>
|
||||||
|
<Avatar account={account} animate={animate} />
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,39 +0,0 @@
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { PureComponent } from 'react';
|
|
||||||
|
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
|
||||||
|
|
||||||
import { autoPlayGif } from 'flavours/glitch/initial_state';
|
|
||||||
|
|
||||||
export default class AvatarOverlay extends PureComponent {
|
|
||||||
|
|
||||||
static propTypes = {
|
|
||||||
account: ImmutablePropTypes.map.isRequired,
|
|
||||||
friend: ImmutablePropTypes.map.isRequired,
|
|
||||||
animate: PropTypes.bool,
|
|
||||||
};
|
|
||||||
|
|
||||||
static defaultProps = {
|
|
||||||
animate: autoPlayGif,
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { account, friend, animate } = this.props;
|
|
||||||
|
|
||||||
const baseStyle = {
|
|
||||||
backgroundImage: `url(${account.get(animate ? 'avatar' : 'avatar_static')})`,
|
|
||||||
};
|
|
||||||
|
|
||||||
const overlayStyle = {
|
|
||||||
backgroundImage: `url(${friend.get(animate ? 'avatar' : 'avatar_static')})`,
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className='account__avatar-overlay'>
|
|
||||||
<div className='account__avatar-overlay-base' style={baseStyle} data-avatar-of={`@${account.get('acct')}`} />
|
|
||||||
<div className='account__avatar-overlay-overlay' style={overlayStyle} data-avatar-of={`@${friend.get('acct')}`} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
import { useHovering } from '../hooks/useHovering';
|
||||||
|
import { autoPlayGif } from '../initial_state';
|
||||||
|
import type { Account } from '../types/resources';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
account: Account | undefined; // FIXME: remove `undefined` once we know for sure its always there
|
||||||
|
friend: Account | undefined; // FIXME: remove `undefined` once we know for sure its always there
|
||||||
|
size?: number;
|
||||||
|
baseSize?: number;
|
||||||
|
overlaySize?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const AvatarOverlay: React.FC<Props> = ({
|
||||||
|
account,
|
||||||
|
friend,
|
||||||
|
size = 46,
|
||||||
|
baseSize = 36,
|
||||||
|
overlaySize = 24,
|
||||||
|
}) => {
|
||||||
|
const { hovering, handleMouseEnter, handleMouseLeave } =
|
||||||
|
useHovering(autoPlayGif);
|
||||||
|
const accountSrc = hovering
|
||||||
|
? account?.get('avatar')
|
||||||
|
: account?.get('avatar_static');
|
||||||
|
const friendSrc = hovering
|
||||||
|
? friend?.get('avatar')
|
||||||
|
: friend?.get('avatar_static');
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className='account__avatar-overlay'
|
||||||
|
style={{ width: size, height: size }}
|
||||||
|
onMouseEnter={handleMouseEnter}
|
||||||
|
onMouseLeave={handleMouseLeave}
|
||||||
|
>
|
||||||
|
<div className='account__avatar-overlay-base'>
|
||||||
|
<div
|
||||||
|
className='account__avatar'
|
||||||
|
style={{ width: `${baseSize}px`, height: `${baseSize}px` }}
|
||||||
|
data-avatar-of={`@${account?.get('acct')}`}
|
||||||
|
>
|
||||||
|
{accountSrc && <img src={accountSrc} alt={account?.get('acct')} />}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className='account__avatar-overlay-overlay'>
|
||||||
|
<div
|
||||||
|
className='account__avatar'
|
||||||
|
style={{ width: `${overlaySize}px`, height: `${overlaySize}px` }}
|
||||||
|
data-avatar-of={`@${friend?.get('acct')}`}
|
||||||
|
>
|
||||||
|
{friendSrc && <img src={friendSrc} alt={friend?.get('acct')} />}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
|
@ -6,7 +6,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
|
|
||||||
// Mastodon imports.
|
// Mastodon imports.
|
||||||
import { Avatar } from './avatar';
|
import { Avatar } from './avatar';
|
||||||
import AvatarOverlay from './avatar_overlay';
|
import { AvatarOverlay } from './avatar_overlay';
|
||||||
import { DisplayName } from './display_name';
|
import { DisplayName } from './display_name';
|
||||||
|
|
||||||
export default class StatusHeader extends PureComponent {
|
export default class StatusHeader extends PureComponent {
|
||||||
|
@ -39,7 +39,7 @@ export default class StatusHeader extends PureComponent {
|
||||||
|
|
||||||
let statusAvatar;
|
let statusAvatar;
|
||||||
if (friend === undefined || friend === null) {
|
if (friend === undefined || friend === null) {
|
||||||
statusAvatar = <Avatar account={account} size={48} />;
|
statusAvatar = <Avatar account={account} size={46} />;
|
||||||
} else {
|
} else {
|
||||||
statusAvatar = <AvatarOverlay account={account} friend={friend} />;
|
statusAvatar = <AvatarOverlay account={account} friend={friend} />;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
import { Icon } from 'flavours/glitch/components/icon';
|
import { Icon } from 'flavours/glitch/components/icon';
|
||||||
import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router';
|
import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router';
|
||||||
|
|
||||||
import AvatarOverlay from '../../../components/avatar_overlay';
|
import { AvatarOverlay } from '../../../components/avatar_overlay';
|
||||||
import { DisplayName } from '../../../components/display_name';
|
import { DisplayName } from '../../../components/display_name';
|
||||||
|
|
||||||
class MovedNote extends ImmutablePureComponent {
|
class MovedNote extends ImmutablePureComponent {
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
|
|
||||||
import AvatarOverlay from 'flavours/glitch/components/avatar_overlay';
|
import { AvatarOverlay } from 'flavours/glitch/components/avatar_overlay';
|
||||||
import { RelativeTimestamp } from 'flavours/glitch/components/relative_timestamp';
|
import { RelativeTimestamp } from 'flavours/glitch/components/relative_timestamp';
|
||||||
|
|
||||||
// This needs to be kept in sync with app/models/report.rb
|
// This needs to be kept in sync with app/models/report.rb
|
||||||
|
|
|
@ -85,10 +85,14 @@
|
||||||
|
|
||||||
display: block;
|
display: block;
|
||||||
position: relative;
|
position: relative;
|
||||||
cursor: pointer;
|
overflow: hidden;
|
||||||
width: 36px;
|
|
||||||
height: 36px;
|
img {
|
||||||
background-size: 36px 36px;
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
|
||||||
&-inline {
|
&-inline {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
@ -102,7 +106,7 @@
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
& div {
|
& > div {
|
||||||
@include avatar-radius;
|
@include avatar-radius;
|
||||||
|
|
||||||
float: left;
|
float: left;
|
||||||
|
@ -110,6 +114,11 @@
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.account__avatar {
|
||||||
|
width: 100% !important;
|
||||||
|
height: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
&__label {
|
&__label {
|
||||||
display: block;
|
display: block;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
@ -125,37 +134,13 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.account__avatar-overlay {
|
.account__avatar-overlay {
|
||||||
@include avatar-size(48px);
|
|
||||||
|
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
&-base {
|
|
||||||
@include avatar-radius;
|
|
||||||
@include avatar-size(36px);
|
|
||||||
|
|
||||||
img {
|
|
||||||
@include avatar-radius;
|
|
||||||
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&-overlay {
|
&-overlay {
|
||||||
@include avatar-radius;
|
|
||||||
@include avatar-size(24px);
|
|
||||||
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
inset-inline-end: 0;
|
inset-inline-end: 0;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
|
|
||||||
img {
|
|
||||||
@include avatar-radius;
|
|
||||||
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue