Add support for avatar status badge (presence)
This commit is contained in:
parent
9645391180
commit
e683d70a18
|
@ -1,12 +1,23 @@
|
|||
.avatar {
|
||||
align-items: center;
|
||||
background-color: #AAA;
|
||||
display: flex;
|
||||
border-radius: 50%;
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
font-weight: 100;
|
||||
justify-content: center;
|
||||
object-fit: cover;
|
||||
|
||||
&.avatar-small {
|
||||
height: 28px !important;
|
||||
width: 28px !important;
|
||||
}
|
||||
|
||||
&.avatar-xsmall {
|
||||
height: 16px !important;
|
||||
width: 16px !important;
|
||||
}
|
||||
|
||||
.jitsi-icon {
|
||||
transform: translateY(50%);
|
||||
}
|
||||
}
|
||||
|
||||
.avatar-foreign {
|
||||
|
@ -28,4 +39,28 @@
|
|||
|
||||
.defaultAvatar {
|
||||
opacity: 0.6
|
||||
}
|
||||
|
||||
.avatar-badge {
|
||||
position: relative;
|
||||
|
||||
&-available::after {
|
||||
@include avatarBadge;
|
||||
background-color: $presence-available;
|
||||
}
|
||||
|
||||
&-away::after {
|
||||
@include avatarBadge;
|
||||
background-color: $presence-away;
|
||||
}
|
||||
|
||||
&-busy::after {
|
||||
@include avatarBadge;
|
||||
background-color: $presence-busy;
|
||||
}
|
||||
|
||||
&-idle::after {
|
||||
@include avatarBadge;
|
||||
background-color: $presence-idle;
|
||||
}
|
||||
}
|
|
@ -192,4 +192,17 @@
|
|||
*/
|
||||
@mixin transparentBg($color, $alpha) {
|
||||
background-color: rgba(red($color), green($color), blue($color), $alpha);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Avatar status badge mixin
|
||||
*/
|
||||
@mixin avatarBadge {
|
||||
border-radius: 50%;
|
||||
content: '';
|
||||
display: block;
|
||||
height: 35%;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 35%;
|
||||
}
|
||||
|
|
|
@ -29,6 +29,10 @@ $defaultSideBarFontColor: #44A5FF;
|
|||
$defaultSemiDarkColor: #ACACAC;
|
||||
$defaultDarkColor: #2b3d5c;
|
||||
$defaultWarningColor: rgb(215, 121, 118);
|
||||
$presence-available: rgb(110, 176, 5);
|
||||
$presence-away: rgb(250, 201, 20);
|
||||
$presence-busy: rgb(233, 0, 27);
|
||||
$presence-idle: rgb(172, 172, 172);
|
||||
|
||||
/**
|
||||
* Toolbar
|
||||
|
|
|
@ -22,38 +22,6 @@
|
|||
"@atlaskit/type-helpers": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"@atlaskit/avatar": {
|
||||
"version": "14.1.7",
|
||||
"resolved": "https://registry.npmjs.org/@atlaskit/avatar/-/avatar-14.1.7.tgz",
|
||||
"integrity": "sha512-KGtV0lRr3g+JX3XLZQKDGxGhtbVFRvM/Ku5C+CEJw2uDl1KFY0dJxfr2a/E32bEgUuvmqSL7D3ROrTrlHJ2fMA==",
|
||||
"requires": {
|
||||
"@atlaskit/analytics-next": "^3.1.2",
|
||||
"@atlaskit/theme": "^7.0.1",
|
||||
"@atlaskit/tooltip": "^12.1.13",
|
||||
"@babel/runtime": "^7.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@atlaskit/analytics-next": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@atlaskit/analytics-next/-/analytics-next-3.1.2.tgz",
|
||||
"integrity": "sha512-bkYDvl3Ojsnim+bsc9BALfvOjiL7xdb2rTp/4yqUP9pfidtf5HudbOJ849+dKcRCmk/rFbfB/nhDBRU6rv1Ueg==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.0.0",
|
||||
"babel-runtime": "^6.26.0",
|
||||
"prop-types": "^15.5.10"
|
||||
}
|
||||
},
|
||||
"@atlaskit/theme": {
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@atlaskit/theme/-/theme-7.0.1.tgz",
|
||||
"integrity": "sha512-wxXDnkUablJketNCrQuNUuazufYEA7kv0Y6Yzv6uvqfuyNpWUQt4H1psz/MW8DbZmCdku9dEYbNVK3nFP5TDGg==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.0.0",
|
||||
"prop-types": "^15.5.10"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@atlaskit/blanket": {
|
||||
"version": "8.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@atlaskit/blanket/-/blanket-8.0.3.tgz",
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
"author": "",
|
||||
"readmeFilename": "README.md",
|
||||
"dependencies": {
|
||||
"@atlaskit/avatar": "14.1.7",
|
||||
"@atlaskit/button": "10.1.1",
|
||||
"@atlaskit/checkbox": "5.0.10",
|
||||
"@atlaskit/dropdown-menu": "6.1.25",
|
||||
|
|
|
@ -54,6 +54,11 @@ export type Props = {
|
|||
*/
|
||||
size: number,
|
||||
|
||||
/**
|
||||
* One of the expected status strings (e.g. 'available') to render a badge on the avatar, if necessary.
|
||||
*/
|
||||
status?: ?string,
|
||||
|
||||
/**
|
||||
* URL of the avatar, if any.
|
||||
*/
|
||||
|
@ -117,6 +122,7 @@ class Avatar<P: Props> extends PureComponent<P, State> {
|
|||
colorBase,
|
||||
id,
|
||||
size,
|
||||
status,
|
||||
url
|
||||
} = this.props;
|
||||
const { avatarFailed } = this.state;
|
||||
|
@ -128,6 +134,7 @@ class Avatar<P: Props> extends PureComponent<P, State> {
|
|||
initials: undefined,
|
||||
onAvatarLoadError: undefined,
|
||||
size,
|
||||
status,
|
||||
url: undefined
|
||||
};
|
||||
|
||||
|
|
|
@ -12,6 +12,11 @@ import styles from './styles';
|
|||
|
||||
type Props = AbstractProps & {
|
||||
|
||||
/**
|
||||
* One of the expected status strings (e.g. 'available') to render a badge on the avatar, if necessary.
|
||||
*/
|
||||
status?: ?string,
|
||||
|
||||
/**
|
||||
* External style passed to the componant.
|
||||
*/
|
||||
|
@ -46,18 +51,40 @@ export default class StatelessAvatar extends AbstractStatelessAvatar<Props> {
|
|||
}
|
||||
|
||||
return (
|
||||
<View
|
||||
style = { [
|
||||
styles.avatarContainer(size),
|
||||
style
|
||||
] }>
|
||||
{ avatar }
|
||||
<View>
|
||||
<View
|
||||
style = { [
|
||||
styles.avatarContainer(size),
|
||||
style
|
||||
] }>
|
||||
{ avatar }
|
||||
</View>
|
||||
{ this._renderAvatarStatus() }
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
_isIcon: (?string | ?Object) => boolean
|
||||
|
||||
/**
|
||||
* Renders a badge representing the avatar status.
|
||||
*
|
||||
* @returns {React$Elementaa}
|
||||
*/
|
||||
_renderAvatarStatus() {
|
||||
const { size, status } = this.props;
|
||||
|
||||
if (!status) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<View style = { styles.badgeContainer }>
|
||||
<View style = { styles.badge(size, status) } />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the default avatar.
|
||||
*
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
// @flow
|
||||
|
||||
import { StyleSheet } from 'react-native';
|
||||
|
||||
import { ColorPalette } from '../../../styles';
|
||||
|
||||
const DEFAULT_SIZE = 65;
|
||||
|
@ -27,6 +29,38 @@ export default {
|
|||
};
|
||||
},
|
||||
|
||||
badge: (size: number = DEFAULT_SIZE, status: string) => {
|
||||
let color;
|
||||
|
||||
switch (status) {
|
||||
case 'available':
|
||||
color = 'rgb(110, 176, 5)';
|
||||
break;
|
||||
case 'away':
|
||||
color = 'rgb(250, 201, 20)';
|
||||
break;
|
||||
case 'busy':
|
||||
color = 'rgb(233, 0, 27)';
|
||||
break;
|
||||
case 'idle':
|
||||
color = 'rgb(172, 172, 172)';
|
||||
break;
|
||||
}
|
||||
|
||||
return {
|
||||
backgroundColor: color,
|
||||
borderRadius: size / 2,
|
||||
bottom: 0,
|
||||
height: size * 0.3,
|
||||
position: 'absolute',
|
||||
width: size * 0.3
|
||||
};
|
||||
},
|
||||
|
||||
badgeContainer: {
|
||||
...StyleSheet.absoluteFillObject
|
||||
},
|
||||
|
||||
initialsContainer: {
|
||||
alignItems: 'center',
|
||||
alignSelf: 'stretch',
|
||||
|
|
|
@ -21,7 +21,12 @@ type Props = AbstractProps & {
|
|||
/**
|
||||
* ID of the component to be rendered.
|
||||
*/
|
||||
id?: string
|
||||
id?: string,
|
||||
|
||||
/**
|
||||
* One of the expected status strings (e.g. 'available') to render a badge on the avatar, if necessary.
|
||||
*/
|
||||
status?: ?string
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -40,7 +45,7 @@ export default class StatelessAvatar extends AbstractStatelessAvatar<Props> {
|
|||
if (this._isIcon(url)) {
|
||||
return (
|
||||
<div
|
||||
className = { this._getAvatarClassName() }
|
||||
className = { `${this._getAvatarClassName()} ${this._getBadgeClassName()}` }
|
||||
id = { this.props.id }
|
||||
style = { this._getAvatarStyle(this.props.color) }>
|
||||
<Icon
|
||||
|
@ -52,19 +57,21 @@ export default class StatelessAvatar extends AbstractStatelessAvatar<Props> {
|
|||
|
||||
if (url) {
|
||||
return (
|
||||
<img
|
||||
className = { this._getAvatarClassName() }
|
||||
id = { this.props.id }
|
||||
onError = { this.props.onAvatarLoadError }
|
||||
src = { url }
|
||||
style = { this._getAvatarStyle() } />
|
||||
<div className = { this._getBadgeClassName() }>
|
||||
<img
|
||||
className = { this._getAvatarClassName() }
|
||||
id = { this.props.id }
|
||||
onError = { this.props.onAvatarLoadError }
|
||||
src = { url }
|
||||
style = { this._getAvatarStyle() } />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (initials) {
|
||||
return (
|
||||
<div
|
||||
className = { this._getAvatarClassName() }
|
||||
className = { `${this._getAvatarClassName()} ${this._getBadgeClassName()}` }
|
||||
id = { this.props.id }
|
||||
style = { this._getAvatarStyle(this.props.color) }>
|
||||
<svg
|
||||
|
@ -87,11 +94,13 @@ export default class StatelessAvatar extends AbstractStatelessAvatar<Props> {
|
|||
|
||||
// default avatar
|
||||
return (
|
||||
<img
|
||||
className = { this._getAvatarClassName('defaultAvatar') }
|
||||
id = { this.props.id }
|
||||
src = { this.props.defaultAvatar || 'images/avatar.png' }
|
||||
style = { this._getAvatarStyle() } />
|
||||
<div className = { this._getBadgeClassName() }>
|
||||
<img
|
||||
className = { this._getAvatarClassName('defaultAvatar') }
|
||||
id = { this.props.id }
|
||||
src = { this.props.defaultAvatar || 'images/avatar.png' }
|
||||
style = { this._getAvatarStyle() } />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -122,5 +131,20 @@ export default class StatelessAvatar extends AbstractStatelessAvatar<Props> {
|
|||
return `avatar ${additional || ''} ${this.props.className || ''}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a class name to render a badge on the avatar, if necessary.
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
_getBadgeClassName() {
|
||||
const { status } = this.props;
|
||||
|
||||
if (status) {
|
||||
return `avatar-badge avatar-badge-${status}`;
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
_isIcon: (?string | ?Object) => boolean
|
||||
}
|
||||
|
|
|
@ -23,6 +23,11 @@ type Props = {
|
|||
*/
|
||||
avatarSize?: number,
|
||||
|
||||
/**
|
||||
* One of the expected status strings (e.g. 'available') to render a badge on the avatar, if necessary.
|
||||
*/
|
||||
avatarStatus?: ?string,
|
||||
|
||||
/**
|
||||
* External style to be applied to the avatar (icon).
|
||||
*/
|
||||
|
@ -83,6 +88,7 @@ export default class AvatarListItem extends Component<Props> {
|
|||
const {
|
||||
avatarOnly,
|
||||
avatarSize = AVATAR_SIZE,
|
||||
avatarStatus,
|
||||
avatarStyle
|
||||
} = this.props;
|
||||
const { avatar, colorBase, lines, title } = this.props.item;
|
||||
|
@ -96,6 +102,7 @@ export default class AvatarListItem extends Component<Props> {
|
|||
colorBase = { colorBase }
|
||||
displayName = { title }
|
||||
size = { avatarSize }
|
||||
status = { avatarStatus }
|
||||
style = { avatarStyle }
|
||||
url = { avatar } />
|
||||
{ avatarOnly || <Container style = { styles.listItemDetails }>
|
||||
|
|
|
@ -443,6 +443,7 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
|
|||
<AvatarListItem
|
||||
avatarOnly = { true }
|
||||
avatarSize = { AVATAR_SIZE }
|
||||
avatarStatus = { item.status }
|
||||
avatarStyle = { styles.avatar }
|
||||
avatarTextStyle = { styles.avatarText }
|
||||
item = { renderableItem }
|
||||
|
@ -497,6 +498,7 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
|
|||
style = { styles.itemWrapper }>
|
||||
<AvatarListItem
|
||||
avatarSize = { AVATAR_SIZE }
|
||||
avatarStatus = { item.status }
|
||||
avatarStyle = { styles.avatar }
|
||||
avatarTextStyle = { styles.avatarText }
|
||||
item = { renderableItem }
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
// @flow
|
||||
|
||||
import Avatar from '@atlaskit/avatar';
|
||||
import InlineMessage from '@atlaskit/inline-message';
|
||||
import React from 'react';
|
||||
import type { Dispatch } from 'redux';
|
||||
|
||||
import { createInviteDialogEvent, sendAnalytics } from '../../../../analytics';
|
||||
import { Avatar } from '../../../../base/avatar';
|
||||
import { Dialog, hideDialog } from '../../../../base/dialog';
|
||||
import { translate, translateToHTML } from '../../../../base/i18n';
|
||||
import { Icon, IconPhone } from '../../../../base/icons';
|
||||
|
@ -289,13 +289,15 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
|
|||
return {
|
||||
content: user.name,
|
||||
elemBefore: <Avatar
|
||||
size = 'small'
|
||||
src = { user.avatar } />,
|
||||
className = { 'avatar-small' }
|
||||
status = { user.status }
|
||||
url = { user.avatar } />,
|
||||
item: user,
|
||||
tag: {
|
||||
elemBefore: <Avatar
|
||||
size = 'xsmall'
|
||||
src = { user.avatar } />
|
||||
className = { 'avatar-xsmall' }
|
||||
status = { user.status }
|
||||
url = { user.avatar } />
|
||||
},
|
||||
value: user.id || user.user_id
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue