RN-466 DND status support (#1115)
* DND status support * Feedback review
This commit is contained in:
parent
bc4df84519
commit
380f5d884a
15 changed files with 284 additions and 84 deletions
|
|
@ -9,7 +9,7 @@ import {
|
|||
} from 'react-native';
|
||||
import Icon from 'react-native-vector-icons/FontAwesome';
|
||||
|
||||
import {OnlineStatus, AwayStatus, OfflineStatus} from 'app/components/status_icons';
|
||||
import {AwayAvatar, DndAvatar, OfflineAvatar, OnlineAvatar} from 'app/components/status_icons';
|
||||
|
||||
import {General} from 'mattermost-redux/constants';
|
||||
|
||||
|
|
@ -90,30 +90,43 @@ export default class ChannelIcon extends React.PureComponent {
|
|||
</View>
|
||||
);
|
||||
} else if (type === General.DM_CHANNEL) {
|
||||
if (status === General.ONLINE) {
|
||||
switch (status) {
|
||||
case General.AWAY:
|
||||
icon = (
|
||||
<OnlineStatus
|
||||
width={size}
|
||||
height={size}
|
||||
color={theme.onlineIndicator}
|
||||
/>
|
||||
);
|
||||
} else if (status === General.AWAY) {
|
||||
icon = (
|
||||
<AwayStatus
|
||||
<AwayAvatar
|
||||
width={size}
|
||||
height={size}
|
||||
color={theme.awayIndicator}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
break;
|
||||
case General.DND:
|
||||
icon = (
|
||||
<OfflineStatus
|
||||
<DndAvatar
|
||||
width={size}
|
||||
height={size}
|
||||
color={theme.dndIndicator}
|
||||
/>
|
||||
);
|
||||
break;
|
||||
case General.ONLINE:
|
||||
icon = (
|
||||
<OnlineAvatar
|
||||
width={size}
|
||||
height={size}
|
||||
color={theme.onlineIndicator}
|
||||
/>
|
||||
);
|
||||
break;
|
||||
default:
|
||||
icon = (
|
||||
<OfflineAvatar
|
||||
width={size}
|
||||
height={size}
|
||||
color={offlineColor}
|
||||
/>
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -77,7 +77,6 @@ class ChannelIntro extends PureComponent {
|
|||
size={64}
|
||||
statusBorderWidth={2}
|
||||
statusSize={25}
|
||||
statusIconSize={15}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
));
|
||||
|
|
|
|||
|
|
@ -4,8 +4,10 @@
|
|||
import React, {PureComponent} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {Image, Platform, View} from 'react-native';
|
||||
import Icon from 'react-native-vector-icons/FontAwesome';
|
||||
|
||||
import {General} from 'mattermost-redux/constants';
|
||||
|
||||
import {AwayIcon, DndIcon, OfflineIcon, OnlineIcon} from 'app/components/status_icons';
|
||||
import {makeStyleSheetFromTheme} from 'app/utils/theme';
|
||||
|
||||
import placeholder from 'assets/images/profile.jpg';
|
||||
|
|
@ -13,8 +15,10 @@ import placeholder from 'assets/images/profile.jpg';
|
|||
import {Client4} from 'mattermost-redux/client';
|
||||
|
||||
const statusToIcon = {
|
||||
online: 'check',
|
||||
away: 'minus'
|
||||
away: AwayIcon,
|
||||
dnd: DndIcon,
|
||||
offline: OfflineIcon,
|
||||
online: OnlineIcon
|
||||
};
|
||||
|
||||
const STATUS_BUFFER = Platform.select({
|
||||
|
|
@ -27,7 +31,6 @@ export default class ProfilePicture extends PureComponent {
|
|||
size: PropTypes.number,
|
||||
statusBorderWidth: PropTypes.number,
|
||||
statusSize: PropTypes.number,
|
||||
statusIconSize: PropTypes.number,
|
||||
user: PropTypes.object,
|
||||
status: PropTypes.string,
|
||||
theme: PropTypes.object.isRequired,
|
||||
|
|
@ -39,8 +42,7 @@ export default class ProfilePicture extends PureComponent {
|
|||
static defaultProps = {
|
||||
size: 128,
|
||||
statusBorderWidth: 2,
|
||||
statusSize: 14,
|
||||
statusIconSize: 8
|
||||
statusSize: 14
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
|
|
@ -50,38 +52,46 @@ export default class ProfilePicture extends PureComponent {
|
|||
}
|
||||
|
||||
render() {
|
||||
const style = getStyleSheet(this.props.theme);
|
||||
const {theme} = this.props;
|
||||
const style = getStyleSheet(theme);
|
||||
|
||||
let pictureUrl;
|
||||
if (this.props.user) {
|
||||
pictureUrl = Client4.getProfilePictureUrl(this.props.user.id, this.props.user.last_picture_update);
|
||||
}
|
||||
|
||||
let statusIcon;
|
||||
let Icon;
|
||||
let iconColor;
|
||||
if (this.props.status && statusToIcon[this.props.status]) {
|
||||
statusIcon = (
|
||||
<Icon
|
||||
style={style.status}
|
||||
name={statusToIcon[this.props.status]}
|
||||
size={this.props.statusIconSize}
|
||||
/>
|
||||
);
|
||||
Icon = statusToIcon[this.props.status];
|
||||
|
||||
switch (this.props.status) {
|
||||
case General.AWAY:
|
||||
iconColor = theme.awayIndicator;
|
||||
break;
|
||||
case General.DND:
|
||||
iconColor = theme.dndIndicator;
|
||||
break;
|
||||
case General.ONLINE:
|
||||
iconColor = theme.onlineIndicator;
|
||||
break;
|
||||
default:
|
||||
iconColor = theme.centerChannelColor;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
statusIcon = (
|
||||
<View
|
||||
style={[style.offlineIcon, {
|
||||
borderRadius: this.props.statusSize / 2,
|
||||
height: this.props.statusSize - this.props.statusBorderWidth,
|
||||
width: this.props.statusSize - this.props.statusBorderWidth,
|
||||
borderWidth: Platform.select({
|
||||
ios: this.props.statusBorderWidth,
|
||||
android: this.props.statusBorderWidth / 2
|
||||
})
|
||||
}]}
|
||||
/>
|
||||
);
|
||||
Icon = statusToIcon.offline;
|
||||
iconColor = theme.centerChannelColor;
|
||||
}
|
||||
|
||||
const statusIcon = (
|
||||
<Icon
|
||||
height={this.props.statusSize}
|
||||
width={this.props.statusSize}
|
||||
color={iconColor}
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<View style={{width: this.props.size + STATUS_BUFFER, height: this.props.size + STATUS_BUFFER}}>
|
||||
<Image
|
||||
|
|
@ -91,32 +101,8 @@ export default class ProfilePicture extends PureComponent {
|
|||
defaultSource={placeholder}
|
||||
/>
|
||||
{this.props.status &&
|
||||
<View
|
||||
style={[
|
||||
style.statusWrapper,
|
||||
{
|
||||
width: this.props.statusSize,
|
||||
height: this.props.statusSize,
|
||||
borderWidth: this.props.statusBorderWidth,
|
||||
borderRadius: this.props.statusSize / 2,
|
||||
borderColor: this.props.theme.centerChannelBg
|
||||
}
|
||||
]}
|
||||
>
|
||||
<View
|
||||
style={[
|
||||
style.statusContainer,
|
||||
{
|
||||
width: this.props.statusSize - this.props.statusBorderWidth,
|
||||
height: this.props.statusSize - this.props.statusBorderWidth,
|
||||
borderRadius: (this.props.statusSize - this.props.statusBorderWidth) / 2,
|
||||
padding: this.props.statusBorderWidth
|
||||
},
|
||||
style[this.props.status]
|
||||
]}
|
||||
>
|
||||
{statusIcon}
|
||||
</View>
|
||||
<View style={[style.statusWrapper, {borderRadius: this.props.statusSize / 2}]}>
|
||||
{statusIcon}
|
||||
</View>
|
||||
}
|
||||
</View>
|
||||
|
|
@ -131,13 +117,9 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
|||
bottom: 0,
|
||||
right: 0,
|
||||
overflow: 'hidden',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
},
|
||||
statusContainer: {
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
overflow: 'hidden'
|
||||
backgroundColor: theme.centerChannelBg
|
||||
},
|
||||
status: {
|
||||
color: theme.centerChannelBg
|
||||
|
|
@ -148,6 +130,9 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
|||
away: {
|
||||
backgroundColor: theme.awayIndicator
|
||||
},
|
||||
dnd: {
|
||||
backgroundColor: 'red'
|
||||
},
|
||||
offline: {
|
||||
backgroundColor: theme.centerChannelBg
|
||||
},
|
||||
|
|
|
|||
49
app/components/status_icons/away_avatar.js
Normal file
49
app/components/status_icons/away_avatar.js
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import React, {PureComponent} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {View} from 'react-native';
|
||||
import Svg, {
|
||||
Circle,
|
||||
G,
|
||||
Path
|
||||
} from 'react-native-svg';
|
||||
|
||||
export default class AwayAvatar extends PureComponent {
|
||||
static propTypes = {
|
||||
width: PropTypes.number.isRequired,
|
||||
height: PropTypes.number.isRequired,
|
||||
color: PropTypes.string.isRequired
|
||||
};
|
||||
|
||||
render() {
|
||||
const {color, height, width} = this.props;
|
||||
return (
|
||||
<View style={{height, width, alignItems: 'flex-start'}}>
|
||||
<Svg
|
||||
width={width}
|
||||
height={height}
|
||||
viewBox='0 0 12 12'
|
||||
>
|
||||
<G transform='matrix(1,0,0,1,299,-391)'>
|
||||
<Circle
|
||||
cx='-294.5'
|
||||
cy='394'
|
||||
r='2.5'
|
||||
fill={color}
|
||||
/>
|
||||
<Path
|
||||
d='M-294.3,399.7C-294.3,399.3 -294.2,398.9 -294.1,398.5C-294.2,398.5 -294.3,398.5 -294.5,398.5C-297,398.5 -297,396.5 -297,396.5C-297,396.5 -298,396.6 -298.2,397C-298.6,397.6 -298.8,398.7 -298.9,399.5C-298.9,399.6 -299,400 -298.9,400.1C-298.7,401.4 -296.7,402.4 -294.5,402.5L-294.3,402.5C-294,402.5 -293.6,402.5 -293.3,402.4C-293.9,401.6 -294.3,400.7 -294.3,399.7Z'
|
||||
fill={color}
|
||||
/>
|
||||
</G>
|
||||
<Path
|
||||
d='M8.415,5C8.614,5 8.775,5.161 8.775,5.36L8.775,8.352L11.49,10.334C11.651,10.451 11.686,10.677 11.569,10.837L10.932,11.709C10.814,11.87 10.589,11.905 10.429,11.788L7.261,9.475C7.243,9.463 7.226,9.451 7.21,9.437L7.123,9.374C7.009,9.291 6.959,9.154 6.98,9.024C6.977,8.999 6.975,8.973 6.975,8.946L6.975,5.36C6.975,5.161 7.137,5 7.335,5L8.415,5Z'
|
||||
fill={color}
|
||||
/>
|
||||
</Svg>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
37
app/components/status_icons/away_icon.js
Normal file
37
app/components/status_icons/away_icon.js
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import React, {PureComponent} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {View} from 'react-native';
|
||||
import Svg, {
|
||||
Path
|
||||
} from 'react-native-svg';
|
||||
|
||||
export default class AwayIcon extends PureComponent {
|
||||
static propTypes = {
|
||||
width: PropTypes.number.isRequired,
|
||||
height: PropTypes.number.isRequired,
|
||||
color: PropTypes.string.isRequired
|
||||
};
|
||||
|
||||
render() {
|
||||
const {color, height, width} = this.props;
|
||||
|
||||
return (
|
||||
<View style={{height, width, alignItems: 'flex-start'}}>
|
||||
<Svg
|
||||
width={width}
|
||||
height={height}
|
||||
viewBox='0 0 20 20'
|
||||
>
|
||||
<Path
|
||||
d='M10,0C15.519,0 20,4.481 20,10C20,15.519 15.519,20 10,20C4.481,20 0,15.519 0,10C0,4.481 4.481,0 10,0ZM10.27,3C10.949,3 11.5,3.586 11.5,4.307L11.5,9.379L15.002,12.881C15.492,13.37 15.499,14.158 15.019,14.638L14.638,15.019C14.158,15.499 13.37,15.492 12.881,15.002L8.887,11.008C8.739,10.861 8.636,10.686 8.576,10.501C8.528,10.402 8.5,10.299 8.5,10.193L8.5,4.307C8.5,3.586 9.051,3 9.73,3L10.27,3Z'
|
||||
fill={color}
|
||||
fillRule='evenodd'
|
||||
/>
|
||||
</Svg>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import React, {PureComponent} from 'react';
|
||||
|
|
@ -10,7 +10,7 @@ import Svg, {
|
|||
Path
|
||||
} from 'react-native-svg';
|
||||
|
||||
export default class AwayStatus extends PureComponent {
|
||||
export default class DndAvatar extends PureComponent {
|
||||
static propTypes = {
|
||||
width: PropTypes.number.isRequired,
|
||||
height: PropTypes.number.isRequired,
|
||||
35
app/components/status_icons/dnd_icon.js
Normal file
35
app/components/status_icons/dnd_icon.js
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import React, {PureComponent} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {View} from 'react-native';
|
||||
import Svg, {
|
||||
Path
|
||||
} from 'react-native-svg';
|
||||
export default class DndIcon extends PureComponent {
|
||||
static propTypes = {
|
||||
width: PropTypes.number.isRequired,
|
||||
height: PropTypes.number.isRequired,
|
||||
color: PropTypes.string.isRequired
|
||||
};
|
||||
|
||||
render() {
|
||||
const {color, height, width} = this.props;
|
||||
return (
|
||||
<View style={{height, width, alignItems: 'flex-start'}}>
|
||||
<Svg
|
||||
width={width}
|
||||
height={height}
|
||||
viewBox='0 0 20 20'
|
||||
>
|
||||
<Path
|
||||
d='M10,0c5.519,0 10,4.481 10,10c0,5.519 -4.481,10 -10,10c-5.519,0 -10,-4.481 -10,-10c0,-5.519 4.481,-10 10,-10Zm5.25,8.5l-10.5,0c-0.414,0 -0.75,0.336 -0.75,0.75l0,1.5c0,0.414 0.336,0.75 0.75,0.75l10.5,0c0.414,0 0.75,-0.336 0.75,-0.75l0,-1.5c0,-0.414 -0.336,-0.75 -0.75,-0.75Z'
|
||||
fill={color}
|
||||
fillRule='evenodd'
|
||||
/>
|
||||
</Svg>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,12 +1,22 @@
|
|||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import OnlineStatus from './online';
|
||||
import AwayStatus from './away';
|
||||
import OfflineStatus from './offline';
|
||||
import AwayAvatar from './away_avatar';
|
||||
import AwayIcon from './away_icon';
|
||||
import DndAvatar from './dnd_avatar';
|
||||
import DndIcon from './dnd_icon';
|
||||
import OfflineAvatar from './offline_avatar';
|
||||
import OfflineIcon from './offline_icon';
|
||||
import OnlineAvatar from './online_avatar';
|
||||
import OnlineIcon from './online_icon';
|
||||
|
||||
export {
|
||||
OnlineStatus,
|
||||
AwayStatus,
|
||||
OfflineStatus
|
||||
AwayAvatar,
|
||||
AwayIcon,
|
||||
DndAvatar,
|
||||
DndIcon,
|
||||
OfflineAvatar,
|
||||
OfflineIcon,
|
||||
OnlineAvatar,
|
||||
OnlineIcon
|
||||
};
|
||||
|
|
|
|||
37
app/components/status_icons/offline_icon.js
Normal file
37
app/components/status_icons/offline_icon.js
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import React, {PureComponent} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {View} from 'react-native';
|
||||
import Svg, {
|
||||
Path
|
||||
} from 'react-native-svg';
|
||||
|
||||
export default class OfflineIcon extends PureComponent {
|
||||
static propTypes = {
|
||||
width: PropTypes.number.isRequired,
|
||||
height: PropTypes.number.isRequired,
|
||||
color: PropTypes.string.isRequired
|
||||
};
|
||||
|
||||
render() {
|
||||
const {color, height, width} = this.props;
|
||||
return (
|
||||
<View style={{height, width, alignItems: 'flex-start'}}>
|
||||
<Svg
|
||||
width={width}
|
||||
height={height}
|
||||
viewBox='0 0 20 20'
|
||||
>
|
||||
<Path
|
||||
d='M10,0c5.519,0 10,4.481 10,10c0,5.519 -4.481,10 -10,10c-5.519,0 -10,-4.481 -10,-10c0,-5.519 4.481,-10 10,-10Zm0,2c4.415,0 8,3.585 8,8c0,4.415 -3.585,8 -8,8c-4.415,0 -8,-3.585 -8,-8c0,-4.415 3.585,-8 8,-8Z'
|
||||
fill={color}
|
||||
fillOpacity={0.5}
|
||||
fillRule='evenodd'
|
||||
/>
|
||||
</Svg>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
36
app/components/status_icons/online_icon.js
Normal file
36
app/components/status_icons/online_icon.js
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import React, {PureComponent} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {View} from 'react-native';
|
||||
import Svg, {
|
||||
Path
|
||||
} from 'react-native-svg';
|
||||
|
||||
export default class OnlineIcon extends PureComponent {
|
||||
static propTypes = {
|
||||
width: PropTypes.number.isRequired,
|
||||
height: PropTypes.number.isRequired,
|
||||
color: PropTypes.string.isRequired
|
||||
};
|
||||
|
||||
render() {
|
||||
const {color, height, width} = this.props;
|
||||
return (
|
||||
<View style={{height, width, alignItems: 'flex-start'}}>
|
||||
<Svg
|
||||
width={width}
|
||||
height={height}
|
||||
viewBox='0 0 20 20'
|
||||
>
|
||||
<Path
|
||||
d='M10,0c5.519,0 10,4.481 10,10c0,5.519 -4.481,10 -10,10c-5.519,0 -10,-4.481 -10,-10c0,-5.519 4.481,-10 10,-10Zm6.19,7.18c0,0.208 -0.075,0.384 -0.224,0.53l-5.782,5.64l-1.087,1.059c-0.149,0.146 -0.33,0.218 -0.543,0.218c-0.213,0 -0.394,-0.072 -0.543,-0.218l-1.086,-1.059l-2.891,-2.82c-0.149,-0.146 -0.224,-0.322 -0.224,-0.53c0,-0.208 0.075,-0.384 0.224,-0.53l1.086,-1.059c0.149,-0.146 0.33,-0.218 0.543,-0.218c0.213,0 0.394,0.072 0.543,0.218l2.348,2.298l5.24,-5.118c0.149,-0.146 0.33,-0.218 0.543,-0.218c0.213,0 0.394,0.072 0.543,0.218l1.086,1.059c0.149,0.146 0.224,0.322 0.224,0.53Z'
|
||||
fill={color}
|
||||
fillRule='evenodd'
|
||||
/>
|
||||
</Svg>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -160,7 +160,6 @@ class UserProfile extends PureComponent {
|
|||
size={150}
|
||||
statusBorderWidth={6}
|
||||
statusSize={40}
|
||||
statusIconSize={18}
|
||||
/>
|
||||
{this.getDisplayName()}
|
||||
<Text style={style.username}>{`@${user.username}`}</Text>
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@
|
|||
"react-native-passcode-status": "1.1.0",
|
||||
"react-native-sentry": "0.15.1",
|
||||
"react-native-status-bar-size": "0.3.2",
|
||||
"react-native-svg": "5.4.2",
|
||||
"react-native-svg": "6.0.0",
|
||||
"react-native-tooltip": "enahum/react-native-tooltip",
|
||||
"react-native-vector-icons": "4.4.2",
|
||||
"react-native-youtube": "1.0.1",
|
||||
|
|
|
|||
|
|
@ -3938,7 +3938,7 @@ makeerror@1.0.x:
|
|||
|
||||
mattermost-redux@mattermost/mattermost-redux#master:
|
||||
version "1.0.1"
|
||||
resolved "https://codeload.github.com/mattermost/mattermost-redux/tar.gz/c00dc5a7464d5a96e0370b8d210a3b1b0449327b"
|
||||
resolved "https://codeload.github.com/mattermost/mattermost-redux/tar.gz/5c0383e2ab08b4419c8a99b79f402ab9ba1fa6c8"
|
||||
dependencies:
|
||||
deep-equal "1.0.1"
|
||||
form-data "2.3.1"
|
||||
|
|
|
|||
Loading…
Reference in a new issue