浏览代码

Add support for avatar status badge (presence)

j8
Bettenbuk Zoltan 5 年前
父节点
当前提交
e683d70a18

+ 38
- 3
css/_avatar.scss 查看文件

1
 .avatar {
1
 .avatar {
2
-    align-items: center;
3
     background-color: #AAA;
2
     background-color: #AAA;
4
-    display: flex;
5
     border-radius: 50%;
3
     border-radius: 50%;
6
     color: rgba(255, 255, 255, 0.6);
4
     color: rgba(255, 255, 255, 0.6);
7
     font-weight: 100;
5
     font-weight: 100;
8
-    justify-content: center;
9
     object-fit: cover;
6
     object-fit: cover;
7
+
8
+    &.avatar-small {
9
+        height: 28px !important;
10
+        width: 28px !important;
11
+    }
12
+
13
+    &.avatar-xsmall {
14
+        height: 16px !important;
15
+        width: 16px !important;
16
+    }
17
+
18
+    .jitsi-icon {
19
+        transform: translateY(50%);
20
+    }
10
 }
21
 }
11
 
22
 
12
 .avatar-foreign {
23
 .avatar-foreign {
28
 
39
 
29
 .defaultAvatar {
40
 .defaultAvatar {
30
     opacity: 0.6
41
     opacity: 0.6
42
+}
43
+
44
+.avatar-badge {
45
+    position: relative;
46
+
47
+    &-available::after {
48
+        @include avatarBadge;
49
+        background-color: $presence-available;
50
+    }
51
+
52
+    &-away::after {
53
+        @include avatarBadge;
54
+        background-color: $presence-away;
55
+    }
56
+
57
+    &-busy::after {
58
+        @include avatarBadge;
59
+        background-color: $presence-busy;
60
+    }
61
+
62
+    &-idle::after {
63
+        @include avatarBadge;
64
+        background-color: $presence-idle;
65
+    }
31
 }
66
 }

+ 14
- 1
css/_mixins.scss 查看文件

192
  */
192
  */
193
 @mixin transparentBg($color, $alpha) {
193
 @mixin transparentBg($color, $alpha) {
194
     background-color: rgba(red($color), green($color), blue($color), $alpha);
194
     background-color: rgba(red($color), green($color), blue($color), $alpha);
195
-}
195
+}
196
+
197
+/**
198
+ * Avatar status badge mixin
199
+ */
200
+@mixin avatarBadge {
201
+    border-radius: 50%;
202
+    content: '';
203
+    display: block;
204
+    height: 35%;
205
+    position: absolute;
206
+    bottom: 0;
207
+    width: 35%;
208
+}

+ 4
- 0
css/_variables.scss 查看文件

29
 $defaultSemiDarkColor: #ACACAC;
29
 $defaultSemiDarkColor: #ACACAC;
30
 $defaultDarkColor: #2b3d5c;
30
 $defaultDarkColor: #2b3d5c;
31
 $defaultWarningColor: rgb(215, 121, 118);
31
 $defaultWarningColor: rgb(215, 121, 118);
32
+$presence-available: rgb(110, 176, 5);
33
+$presence-away: rgb(250, 201, 20);
34
+$presence-busy: rgb(233, 0, 27);
35
+$presence-idle: rgb(172, 172, 172);
32
 
36
 
33
 /**
37
 /**
34
  * Toolbar
38
  * Toolbar

+ 0
- 32
package-lock.json 查看文件

22
         "@atlaskit/type-helpers": "^2.0.0"
22
         "@atlaskit/type-helpers": "^2.0.0"
23
       }
23
       }
24
     },
24
     },
25
-    "@atlaskit/avatar": {
26
-      "version": "14.1.7",
27
-      "resolved": "https://registry.npmjs.org/@atlaskit/avatar/-/avatar-14.1.7.tgz",
28
-      "integrity": "sha512-KGtV0lRr3g+JX3XLZQKDGxGhtbVFRvM/Ku5C+CEJw2uDl1KFY0dJxfr2a/E32bEgUuvmqSL7D3ROrTrlHJ2fMA==",
29
-      "requires": {
30
-        "@atlaskit/analytics-next": "^3.1.2",
31
-        "@atlaskit/theme": "^7.0.1",
32
-        "@atlaskit/tooltip": "^12.1.13",
33
-        "@babel/runtime": "^7.0.0"
34
-      },
35
-      "dependencies": {
36
-        "@atlaskit/analytics-next": {
37
-          "version": "3.1.2",
38
-          "resolved": "https://registry.npmjs.org/@atlaskit/analytics-next/-/analytics-next-3.1.2.tgz",
39
-          "integrity": "sha512-bkYDvl3Ojsnim+bsc9BALfvOjiL7xdb2rTp/4yqUP9pfidtf5HudbOJ849+dKcRCmk/rFbfB/nhDBRU6rv1Ueg==",
40
-          "requires": {
41
-            "@babel/runtime": "^7.0.0",
42
-            "babel-runtime": "^6.26.0",
43
-            "prop-types": "^15.5.10"
44
-          }
45
-        },
46
-        "@atlaskit/theme": {
47
-          "version": "7.0.1",
48
-          "resolved": "https://registry.npmjs.org/@atlaskit/theme/-/theme-7.0.1.tgz",
49
-          "integrity": "sha512-wxXDnkUablJketNCrQuNUuazufYEA7kv0Y6Yzv6uvqfuyNpWUQt4H1psz/MW8DbZmCdku9dEYbNVK3nFP5TDGg==",
50
-          "requires": {
51
-            "@babel/runtime": "^7.0.0",
52
-            "prop-types": "^15.5.10"
53
-          }
54
-        }
55
-      }
56
-    },
57
     "@atlaskit/blanket": {
25
     "@atlaskit/blanket": {
58
       "version": "8.0.3",
26
       "version": "8.0.3",
59
       "resolved": "https://registry.npmjs.org/@atlaskit/blanket/-/blanket-8.0.3.tgz",
27
       "resolved": "https://registry.npmjs.org/@atlaskit/blanket/-/blanket-8.0.3.tgz",

+ 0
- 1
package.json 查看文件

15
   "author": "",
15
   "author": "",
16
   "readmeFilename": "README.md",
16
   "readmeFilename": "README.md",
17
   "dependencies": {
17
   "dependencies": {
18
-    "@atlaskit/avatar": "14.1.7",
19
     "@atlaskit/button": "10.1.1",
18
     "@atlaskit/button": "10.1.1",
20
     "@atlaskit/checkbox": "5.0.10",
19
     "@atlaskit/checkbox": "5.0.10",
21
     "@atlaskit/dropdown-menu": "6.1.25",
20
     "@atlaskit/dropdown-menu": "6.1.25",

+ 7
- 0
react/features/base/avatar/components/Avatar.js 查看文件

54
      */
54
      */
55
     size: number,
55
     size: number,
56
 
56
 
57
+    /**
58
+     * One of the expected status strings (e.g. 'available') to render a badge on the avatar, if necessary.
59
+     */
60
+    status?: ?string,
61
+
57
     /**
62
     /**
58
      * URL of the avatar, if any.
63
      * URL of the avatar, if any.
59
      */
64
      */
117
             colorBase,
122
             colorBase,
118
             id,
123
             id,
119
             size,
124
             size,
125
+            status,
120
             url
126
             url
121
         } = this.props;
127
         } = this.props;
122
         const { avatarFailed } = this.state;
128
         const { avatarFailed } = this.state;
128
             initials: undefined,
134
             initials: undefined,
129
             onAvatarLoadError: undefined,
135
             onAvatarLoadError: undefined,
130
             size,
136
             size,
137
+            status,
131
             url: undefined
138
             url: undefined
132
         };
139
         };
133
 
140
 

+ 33
- 6
react/features/base/avatar/components/native/StatelessAvatar.js 查看文件

12
 
12
 
13
 type Props = AbstractProps & {
13
 type Props = AbstractProps & {
14
 
14
 
15
+    /**
16
+     * One of the expected status strings (e.g. 'available') to render a badge on the avatar, if necessary.
17
+     */
18
+    status?: ?string,
19
+
15
     /**
20
     /**
16
      * External style passed to the componant.
21
      * External style passed to the componant.
17
      */
22
      */
46
         }
51
         }
47
 
52
 
48
         return (
53
         return (
49
-            <View
50
-                style = { [
51
-                    styles.avatarContainer(size),
52
-                    style
53
-                ] }>
54
-                { avatar }
54
+            <View>
55
+                <View
56
+                    style = { [
57
+                        styles.avatarContainer(size),
58
+                        style
59
+                    ] }>
60
+                    { avatar }
61
+                </View>
62
+                { this._renderAvatarStatus() }
55
             </View>
63
             </View>
56
         );
64
         );
57
     }
65
     }
58
 
66
 
59
     _isIcon: (?string | ?Object) => boolean
67
     _isIcon: (?string | ?Object) => boolean
60
 
68
 
69
+    /**
70
+     * Renders a badge representing the avatar status.
71
+     *
72
+     * @returns {React$Elementaa}
73
+     */
74
+    _renderAvatarStatus() {
75
+        const { size, status } = this.props;
76
+
77
+        if (!status) {
78
+            return null;
79
+        }
80
+
81
+        return (
82
+            <View style = { styles.badgeContainer }>
83
+                <View style = { styles.badge(size, status) } />
84
+            </View>
85
+        );
86
+    }
87
+
61
     /**
88
     /**
62
      * Renders the default avatar.
89
      * Renders the default avatar.
63
      *
90
      *

+ 34
- 0
react/features/base/avatar/components/native/styles.js 查看文件

1
 // @flow
1
 // @flow
2
 
2
 
3
+import { StyleSheet } from 'react-native';
4
+
3
 import { ColorPalette } from '../../../styles';
5
 import { ColorPalette } from '../../../styles';
4
 
6
 
5
 const DEFAULT_SIZE = 65;
7
 const DEFAULT_SIZE = 65;
27
         };
29
         };
28
     },
30
     },
29
 
31
 
32
+    badge: (size: number = DEFAULT_SIZE, status: string) => {
33
+        let color;
34
+
35
+        switch (status) {
36
+        case 'available':
37
+            color = 'rgb(110, 176, 5)';
38
+            break;
39
+        case 'away':
40
+            color = 'rgb(250, 201, 20)';
41
+            break;
42
+        case 'busy':
43
+            color = 'rgb(233, 0, 27)';
44
+            break;
45
+        case 'idle':
46
+            color = 'rgb(172, 172, 172)';
47
+            break;
48
+        }
49
+
50
+        return {
51
+            backgroundColor: color,
52
+            borderRadius: size / 2,
53
+            bottom: 0,
54
+            height: size * 0.3,
55
+            position: 'absolute',
56
+            width: size * 0.3
57
+        };
58
+    },
59
+
60
+    badgeContainer: {
61
+        ...StyleSheet.absoluteFillObject
62
+    },
63
+
30
     initialsContainer: {
64
     initialsContainer: {
31
         alignItems: 'center',
65
         alignItems: 'center',
32
         alignSelf: 'stretch',
66
         alignSelf: 'stretch',

+ 38
- 14
react/features/base/avatar/components/web/StatelessAvatar.js 查看文件

21
     /**
21
     /**
22
      * ID of the component to be rendered.
22
      * ID of the component to be rendered.
23
      */
23
      */
24
-    id?: string
24
+    id?: string,
25
+
26
+    /**
27
+     * One of the expected status strings (e.g. 'available') to render a badge on the avatar, if necessary.
28
+     */
29
+    status?: ?string
25
 };
30
 };
26
 
31
 
27
 /**
32
 /**
40
         if (this._isIcon(url)) {
45
         if (this._isIcon(url)) {
41
             return (
46
             return (
42
                 <div
47
                 <div
43
-                    className = { this._getAvatarClassName() }
48
+                    className = { `${this._getAvatarClassName()} ${this._getBadgeClassName()}` }
44
                     id = { this.props.id }
49
                     id = { this.props.id }
45
                     style = { this._getAvatarStyle(this.props.color) }>
50
                     style = { this._getAvatarStyle(this.props.color) }>
46
                     <Icon
51
                     <Icon
52
 
57
 
53
         if (url) {
58
         if (url) {
54
             return (
59
             return (
55
-                <img
56
-                    className = { this._getAvatarClassName() }
57
-                    id = { this.props.id }
58
-                    onError = { this.props.onAvatarLoadError }
59
-                    src = { url }
60
-                    style = { this._getAvatarStyle() } />
60
+                <div className = { this._getBadgeClassName() }>
61
+                    <img
62
+                        className = { this._getAvatarClassName() }
63
+                        id = { this.props.id }
64
+                        onError = { this.props.onAvatarLoadError }
65
+                        src = { url }
66
+                        style = { this._getAvatarStyle() } />
67
+                </div>
61
             );
68
             );
62
         }
69
         }
63
 
70
 
64
         if (initials) {
71
         if (initials) {
65
             return (
72
             return (
66
                 <div
73
                 <div
67
-                    className = { this._getAvatarClassName() }
74
+                    className = { `${this._getAvatarClassName()} ${this._getBadgeClassName()}` }
68
                     id = { this.props.id }
75
                     id = { this.props.id }
69
                     style = { this._getAvatarStyle(this.props.color) }>
76
                     style = { this._getAvatarStyle(this.props.color) }>
70
                     <svg
77
                     <svg
87
 
94
 
88
         // default avatar
95
         // default avatar
89
         return (
96
         return (
90
-            <img
91
-                className = { this._getAvatarClassName('defaultAvatar') }
92
-                id = { this.props.id }
93
-                src = { this.props.defaultAvatar || 'images/avatar.png' }
94
-                style = { this._getAvatarStyle() } />
97
+            <div className = { this._getBadgeClassName() }>
98
+                <img
99
+                    className = { this._getAvatarClassName('defaultAvatar') }
100
+                    id = { this.props.id }
101
+                    src = { this.props.defaultAvatar || 'images/avatar.png' }
102
+                    style = { this._getAvatarStyle() } />
103
+            </div>
95
         );
104
         );
96
     }
105
     }
97
 
106
 
122
         return `avatar ${additional || ''} ${this.props.className || ''}`;
131
         return `avatar ${additional || ''} ${this.props.className || ''}`;
123
     }
132
     }
124
 
133
 
134
+    /**
135
+     * Generates a class name to render a badge on the avatar, if necessary.
136
+     *
137
+     * @returns {string}
138
+     */
139
+    _getBadgeClassName() {
140
+        const { status } = this.props;
141
+
142
+        if (status) {
143
+            return `avatar-badge avatar-badge-${status}`;
144
+        }
145
+
146
+        return '';
147
+    }
148
+
125
     _isIcon: (?string | ?Object) => boolean
149
     _isIcon: (?string | ?Object) => boolean
126
 }
150
 }

+ 7
- 0
react/features/base/react/components/native/AvatarListItem.js 查看文件

23
      */
23
      */
24
     avatarSize?: number,
24
     avatarSize?: number,
25
 
25
 
26
+    /**
27
+     * One of the expected status strings (e.g. 'available') to render a badge on the avatar, if necessary.
28
+     */
29
+    avatarStatus?: ?string,
30
+
26
     /**
31
     /**
27
      * External style to be applied to the avatar (icon).
32
      * External style to be applied to the avatar (icon).
28
      */
33
      */
83
         const {
88
         const {
84
             avatarOnly,
89
             avatarOnly,
85
             avatarSize = AVATAR_SIZE,
90
             avatarSize = AVATAR_SIZE,
91
+            avatarStatus,
86
             avatarStyle
92
             avatarStyle
87
         } = this.props;
93
         } = this.props;
88
         const { avatar, colorBase, lines, title } = this.props.item;
94
         const { avatar, colorBase, lines, title } = this.props.item;
96
                     colorBase = { colorBase }
102
                     colorBase = { colorBase }
97
                     displayName = { title }
103
                     displayName = { title }
98
                     size = { avatarSize }
104
                     size = { avatarSize }
105
+                    status = { avatarStatus }
99
                     style = { avatarStyle }
106
                     style = { avatarStyle }
100
                     url = { avatar } />
107
                     url = { avatar } />
101
                 { avatarOnly || <Container style = { styles.listItemDetails }>
108
                 { avatarOnly || <Container style = { styles.listItemDetails }>

+ 2
- 0
react/features/invite/components/add-people-dialog/native/AddPeopleDialog.js 查看文件

443
                     <AvatarListItem
443
                     <AvatarListItem
444
                         avatarOnly = { true }
444
                         avatarOnly = { true }
445
                         avatarSize = { AVATAR_SIZE }
445
                         avatarSize = { AVATAR_SIZE }
446
+                        avatarStatus = { item.status }
446
                         avatarStyle = { styles.avatar }
447
                         avatarStyle = { styles.avatar }
447
                         avatarTextStyle = { styles.avatarText }
448
                         avatarTextStyle = { styles.avatarText }
448
                         item = { renderableItem }
449
                         item = { renderableItem }
497
                     style = { styles.itemWrapper }>
498
                     style = { styles.itemWrapper }>
498
                     <AvatarListItem
499
                     <AvatarListItem
499
                         avatarSize = { AVATAR_SIZE }
500
                         avatarSize = { AVATAR_SIZE }
501
+                        avatarStatus = { item.status }
500
                         avatarStyle = { styles.avatar }
502
                         avatarStyle = { styles.avatar }
501
                         avatarTextStyle = { styles.avatarText }
503
                         avatarTextStyle = { styles.avatarText }
502
                         item = { renderableItem }
504
                         item = { renderableItem }

+ 7
- 5
react/features/invite/components/add-people-dialog/web/AddPeopleDialog.js 查看文件

1
 // @flow
1
 // @flow
2
 
2
 
3
-import Avatar from '@atlaskit/avatar';
4
 import InlineMessage from '@atlaskit/inline-message';
3
 import InlineMessage from '@atlaskit/inline-message';
5
 import React from 'react';
4
 import React from 'react';
6
 import type { Dispatch } from 'redux';
5
 import type { Dispatch } from 'redux';
7
 
6
 
8
 import { createInviteDialogEvent, sendAnalytics } from '../../../../analytics';
7
 import { createInviteDialogEvent, sendAnalytics } from '../../../../analytics';
8
+import { Avatar } from '../../../../base/avatar';
9
 import { Dialog, hideDialog } from '../../../../base/dialog';
9
 import { Dialog, hideDialog } from '../../../../base/dialog';
10
 import { translate, translateToHTML } from '../../../../base/i18n';
10
 import { translate, translateToHTML } from '../../../../base/i18n';
11
 import { Icon, IconPhone } from '../../../../base/icons';
11
 import { Icon, IconPhone } from '../../../../base/icons';
289
             return {
289
             return {
290
                 content: user.name,
290
                 content: user.name,
291
                 elemBefore: <Avatar
291
                 elemBefore: <Avatar
292
-                    size = 'small'
293
-                    src = { user.avatar } />,
292
+                    className = { 'avatar-small' }
293
+                    status = { user.status }
294
+                    url = { user.avatar } />,
294
                 item: user,
295
                 item: user,
295
                 tag: {
296
                 tag: {
296
                     elemBefore: <Avatar
297
                     elemBefore: <Avatar
297
-                        size = 'xsmall'
298
-                        src = { user.avatar } />
298
+                        className = { 'avatar-xsmall' }
299
+                        status = { user.status }
300
+                        url = { user.avatar } />
299
                 },
301
                 },
300
                 value: user.id || user.user_id
302
                 value: user.id || user.user_id
301
             };
303
             };

正在加载...
取消
保存