Add support for avatar status badge (presence)

This commit is contained in:
Bettenbuk Zoltan
2019-12-09 11:58:23 +01:00
committed by Zoltan Bettenbuk
parent 9645391180
commit e683d70a18
12 changed files with 184 additions and 62 deletions
@@ -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
};