Explorar el Código

fix: AlwaysOnTop avatar

master
Bettenbuk Zoltan hace 5 años
padre
commit
a04982fd96

+ 26
- 15
react/features/always-on-top/AlwaysOnTop.js Ver fichero

@@ -2,6 +2,11 @@
2 2
 
3 3
 import React, { Component } from 'react';
4 4
 
5
+// We need to reference these files directly to avoid loading things that are not available
6
+// in this environment (e.g. JitsiMeetJS or interfaceConfig)
7
+import StatelessAvatar from '../base/avatar/components/web/StatelessAvatar';
8
+import { getInitials } from '../base/avatar/functions';
9
+
5 10
 import Toolbar from './Toolbar';
6 11
 
7 12
 const { api } = window.alwaysOnTop;
@@ -17,6 +22,7 @@ const TOOLBAR_TIMEOUT = 4000;
17 22
 type State = {
18 23
     avatarURL: string,
19 24
     displayName: string,
25
+    formattedDisplayName: string,
20 26
     isVideoDisplayed: boolean,
21 27
     visible: boolean
22 28
 };
@@ -42,6 +48,7 @@ export default class AlwaysOnTop extends Component<*, State> {
42 48
         this.state = {
43 49
             avatarURL: '',
44 50
             displayName: '',
51
+            formattedDisplayName: '',
45 52
             isVideoDisplayed: true,
46 53
             visible: true
47 54
         };
@@ -78,10 +85,15 @@ export default class AlwaysOnTop extends Component<*, State> {
78 85
      *
79 86
      * @returns {void}
80 87
      */
81
-    _displayNameChangedListener({ formattedDisplayName, id }) {
88
+    _displayNameChangedListener({ displayname, formattedDisplayName, id }) {
82 89
         if (api._getOnStageParticipant() === id
83
-                && formattedDisplayName !== this.state.displayName) {
84
-            this.setState({ displayName: formattedDisplayName });
90
+                && (formattedDisplayName !== this.state.formattedDisplayName
91
+                    || displayname !== this.state.displayName)) {
92
+            // I think the API has a typo using lowercase n for the displayname
93
+            this.setState({
94
+                displayName: displayname,
95
+                formattedDisplayName
96
+            });
85 97
         }
86 98
     }
87 99
 
@@ -112,12 +124,14 @@ export default class AlwaysOnTop extends Component<*, State> {
112 124
     _largeVideoChangedListener() {
113 125
         const userID = api._getOnStageParticipant();
114 126
         const avatarURL = api.getAvatarURL(userID);
115
-        const displayName = api._getFormattedDisplayName(userID);
127
+        const displayName = api.getDisplayName(userID);
128
+        const formattedDisplayName = api._getFormattedDisplayName(userID);
116 129
         const isVideoDisplayed = Boolean(api._getLargeVideo());
117 130
 
118 131
         this.setState({
119 132
             avatarURL,
120 133
             displayName,
134
+            formattedDisplayName,
121 135
             isVideoDisplayed
122 136
         });
123 137
     }
@@ -161,7 +175,7 @@ export default class AlwaysOnTop extends Component<*, State> {
161 175
      * @returns {ReactElement}
162 176
      */
163 177
     _renderVideoNotAvailableScreen() {
164
-        const { avatarURL, displayName, isVideoDisplayed } = this.state;
178
+        const { avatarURL, displayName, formattedDisplayName, isVideoDisplayed } = this.state;
165 179
 
166 180
         if (isVideoDisplayed) {
167 181
             return null;
@@ -169,19 +183,16 @@ export default class AlwaysOnTop extends Component<*, State> {
169 183
 
170 184
         return (
171 185
             <div id = 'videoNotAvailableScreen'>
172
-                {
173
-                    avatarURL
174
-                        ? <div id = 'avatarContainer'>
175
-                            <img
176
-                                id = 'avatar'
177
-                                src = { avatarURL } />
178
-                        </div>
179
-                        : null
180
-                }
186
+                <div id = 'avatarContainer'>
187
+                    <StatelessAvatar
188
+                        id = 'avatar'
189
+                        initials = { getInitials(displayName) }
190
+                        url = { avatarURL } />)
191
+                </div>
181 192
                 <div
182 193
                     className = 'displayname'
183 194
                     id = 'displayname'>
184
-                    { displayName }
195
+                    { formattedDisplayName }
185 196
                 </div>
186 197
             </div>
187 198
         );

+ 37
- 0
react/features/base/avatar/components/AbstractStatelessAvatar.js Ver fichero

@@ -0,0 +1,37 @@
1
+// @flow
2
+
3
+import { PureComponent } from 'react';
4
+
5
+export type Props = {
6
+
7
+    /**
8
+     * Color of the (initials based) avatar, if needed.
9
+     */
10
+    color?: string,
11
+
12
+    /**
13
+     * Initials to be used to render the initials based avatars.
14
+     */
15
+    initials?: string,
16
+
17
+    /**
18
+     * Callback to signal the failure of the loading of the URL.
19
+     */
20
+    onAvatarLoadError?: Function,
21
+
22
+    /**
23
+     * Expected size of the avatar.
24
+     */
25
+    size?: number;
26
+
27
+    /**
28
+     * The URL of the avatar to render.
29
+     */
30
+    url?: ?string
31
+};
32
+
33
+/**
34
+ * Implements an abstract stateless avatar component that renders an avatar purely from what gets passed through
35
+ * props.
36
+ */
37
+export default class AbstractStatelessAvatar<P: Props> extends PureComponent<P> {}

react/features/base/avatar/components/AbstractAvatar.js → react/features/base/avatar/components/Avatar.js Ver fichero

@@ -1,11 +1,14 @@
1 1
 // @flow
2 2
 
3
-import { PureComponent } from 'react';
3
+import React, { PureComponent } from 'react';
4 4
 
5 5
 import { getParticipantById } from '../../participants';
6
+import { connect } from '../../redux';
6 7
 
7 8
 import { getAvatarColor, getInitials } from '../functions';
8 9
 
10
+import { StatelessAvatar } from '.';
11
+
9 12
 export type Props = {
10 13
 
11 14
     /**
@@ -18,6 +21,11 @@ export type Props = {
18 21
      */
19 22
     _loadableAvatarUrl: ?string,
20 23
 
24
+    /**
25
+     * A prop to maintain compatibility with web.
26
+     */
27
+    className?: string,
28
+
21 29
     /**
22 30
      * A string to override the initials to generate a color of. This is handy if you don't want to make
23 31
      * the background color match the string that the initials are generated from.
@@ -30,6 +38,11 @@ export type Props = {
30 38
      */
31 39
     displayName?: string,
32 40
 
41
+    /**
42
+     * ID of the element, if any.
43
+     */
44
+    id?: string,
45
+
33 46
     /**
34 47
      * The ID of the participant to render an avatar for (if it's a participant avatar).
35 48
      */
@@ -41,9 +54,9 @@ export type Props = {
41 54
     size: number,
42 55
 
43 56
     /**
44
-     * URI of the avatar, if any.
57
+     * URL of the avatar, if any.
45 58
      */
46
-    uri: ?string,
59
+    url: ?string,
47 60
 }
48 61
 
49 62
 type State = {
@@ -53,9 +66,9 @@ type State = {
53 66
 export const DEFAULT_SIZE = 65;
54 67
 
55 68
 /**
56
- * Implements an abstract class to render avatars in the app.
69
+ * Implements a class to render avatars in the app.
57 70
  */
58
-export default class AbstractAvatar<P: Props> extends PureComponent<P, State> {
71
+class Avatar<P: Props> extends PureComponent<P, State> {
59 72
     /**
60 73
      * Instantiates a new {@code Component}.
61 74
      *
@@ -77,7 +90,7 @@ export default class AbstractAvatar<P: Props> extends PureComponent<P, State> {
77 90
      * @inheritdoc
78 91
      */
79 92
     componentDidUpdate(prevProps: P) {
80
-        if (prevProps.uri !== this.props.uri) {
93
+        if (prevProps.url !== this.props.url) {
81 94
 
82 95
             // URI changed, so we need to try to fetch it again.
83 96
             // Eslint doesn't like this statement, but based on the React doc, it's safe if it's
@@ -99,25 +112,45 @@ export default class AbstractAvatar<P: Props> extends PureComponent<P, State> {
99 112
         const {
100 113
             _initialsBase,
101 114
             _loadableAvatarUrl,
115
+            className,
102 116
             colorBase,
103
-            uri
117
+            id,
118
+            size,
119
+            url
104 120
         } = this.props;
105 121
         const { avatarFailed } = this.state;
106 122
 
123
+        const avatarProps = {
124
+            className,
125
+            color: undefined,
126
+            id,
127
+            initials: undefined,
128
+            onAvatarLoadError: undefined,
129
+            size,
130
+            url: undefined
131
+        };
132
+
107 133
         // _loadableAvatarUrl is validated that it can be loaded, but uri (if present) is not, so
108 134
         // we still need to do a check for that. And an explicitly provided URI is higher priority than
109 135
         // an avatar URL anyhow.
110
-        if ((uri && !avatarFailed) || _loadableAvatarUrl) {
111
-            return this._renderURLAvatar((!avatarFailed && uri) || _loadableAvatarUrl);
112
-        }
113
-
114
-        const _initials = getInitials(_initialsBase);
115
-
116
-        if (_initials) {
117
-            return this._renderInitialsAvatar(_initials, getAvatarColor(colorBase || _initialsBase));
136
+        const effectiveURL = (!avatarFailed && url) || _loadableAvatarUrl;
137
+
138
+        if (effectiveURL) {
139
+            avatarProps.onAvatarLoadError = this._onAvatarLoadError;
140
+            avatarProps.url = effectiveURL;
141
+        } else {
142
+            const initials = getInitials(_initialsBase);
143
+
144
+            if (initials) {
145
+                avatarProps.color = getAvatarColor(colorBase || _initialsBase);
146
+                avatarProps.initials = initials;
147
+            }
118 148
         }
119 149
 
120
-        return this._renderDefaultAvatar();
150
+        return (
151
+            <StatelessAvatar
152
+                { ...avatarProps } />
153
+        );
121 154
     }
122 155
 
123 156
     _onAvatarLoadError: () => void;
@@ -132,30 +165,6 @@ export default class AbstractAvatar<P: Props> extends PureComponent<P, State> {
132 165
             avatarFailed: true
133 166
         });
134 167
     }
135
-
136
-    /**
137
-     * Function to render the actual, platform specific default avatar component.
138
-     *
139
-     * @returns {React$Element<*>}
140
-     */
141
-    _renderDefaultAvatar: () => React$Element<*>
142
-
143
-    /**
144
-     * Function to render the actual, platform specific initials-based avatar component.
145
-     *
146
-     * @param {string} initials - The initials to use.
147
-     * @param {string} color - The color to use.
148
-     * @returns {React$Element<*>}
149
-     */
150
-    _renderInitialsAvatar: (string, string) => React$Element<*>
151
-
152
-    /**
153
-     * Function to render the actual, platform specific URL-based avatar component.
154
-     *
155
-     * @param {string} uri - The URI of the avatar.
156
-     * @returns {React$Element<*>}
157
-     */
158
-    _renderURLAvatar: ?string => React$Element<*>
159 168
 }
160 169
 
161 170
 /**
@@ -168,10 +177,12 @@ export default class AbstractAvatar<P: Props> extends PureComponent<P, State> {
168 177
 export function _mapStateToProps(state: Object, ownProps: Props) {
169 178
     const { displayName, participantId } = ownProps;
170 179
     const _participant = participantId && getParticipantById(state, participantId);
171
-    const _initialsBase = (_participant && (_participant.name || _participant.email)) || displayName;
180
+    const _initialsBase = (_participant && _participant.name) || displayName;
172 181
 
173 182
     return {
174 183
         _initialsBase,
175 184
         _loadableAvatarUrl: _participant && _participant.loadableAvatarUrl
176 185
     };
177 186
 }
187
+
188
+export default connect(_mapStateToProps)(Avatar);

+ 1
- 0
react/features/base/avatar/components/index.native.js Ver fichero

@@ -1,3 +1,4 @@
1 1
 // @flow
2 2
 
3 3
 export * from './native';
4
+export { default as Avatar } from './Avatar';

+ 1
- 0
react/features/base/avatar/components/index.web.js Ver fichero

@@ -1,3 +1,4 @@
1 1
 // @flow
2 2
 
3 3
 export * from './web';
4
+export { default as Avatar } from './Avatar';

+ 0
- 106
react/features/base/avatar/components/native/Avatar.js Ver fichero

@@ -1,106 +0,0 @@
1
-// @flow
2
-
3
-import React from 'react';
4
-import { Image, Text, View } from 'react-native';
5
-
6
-import { connect } from '../../../redux';
7
-import { type StyleType } from '../../../styles';
8
-
9
-import AbstractAvatar, {
10
-    _mapStateToProps,
11
-    type Props as AbstractProps,
12
-    DEFAULT_SIZE
13
-} from '../AbstractAvatar';
14
-
15
-import RemoteAvatar, { DEFAULT_AVATAR } from './RemoteAvatar';
16
-import styles from './styles';
17
-
18
-type Props = AbstractProps & {
19
-
20
-    /**
21
-     * External style of the component.
22
-     */
23
-    style?: StyleType
24
-}
25
-
26
-/**
27
- * Implements an avatar component that has 4 ways to render an avatar:
28
- *
29
- * - Based on an explicit avatar URI, if provided
30
- * - Gravatar, if there is any
31
- * - Based on initials generated from name or email
32
- * - Default avatar icon, if any of the above fails
33
- */
34
-class Avatar extends AbstractAvatar<Props> {
35
-
36
-    _onAvatarLoadError: () => void;
37
-
38
-    /**
39
-     * Implements {@code AbstractAvatar#_renderDefaultAvatar}.
40
-     *
41
-     * @inheritdoc
42
-     */
43
-    _renderDefaultAvatar() {
44
-        return this._wrapAvatar(
45
-            <Image
46
-                source = { DEFAULT_AVATAR }
47
-                style = { [
48
-                    styles.avatarContent(this.props.size || DEFAULT_SIZE),
49
-                    styles.staticAvatar
50
-                ] } />
51
-        );
52
-    }
53
-
54
-    /**
55
-     * Implements {@code AbstractAvatar#_renderGravatar}.
56
-     *
57
-     * @inheritdoc
58
-     */
59
-    _renderInitialsAvatar(initials, color) {
60
-        return this._wrapAvatar(
61
-            <View
62
-                style = { [
63
-                    styles.initialsContainer,
64
-                    {
65
-                        backgroundColor: color
66
-                    }
67
-                ] }>
68
-                <Text style = { styles.initialsText(this.props.size || DEFAULT_SIZE) }> { initials } </Text>
69
-            </View>
70
-        );
71
-    }
72
-
73
-    /**
74
-     * Implements {@code AbstractAvatar#_renderGravatar}.
75
-     *
76
-     * @inheritdoc
77
-     */
78
-    _renderURLAvatar(uri) {
79
-        return this._wrapAvatar(
80
-            <RemoteAvatar
81
-                onError = { this._onAvatarLoadError }
82
-                size = { this.props.size || DEFAULT_SIZE }
83
-                uri = { uri } />
84
-        );
85
-    }
86
-
87
-    /**
88
-     * Wraps an avatar into a common wrapper.
89
-     *
90
-     * @param {React#Component} avatar - The avatar component.
91
-     * @returns {React#Component}
92
-     */
93
-    _wrapAvatar(avatar) {
94
-        return (
95
-            <View
96
-                style = { [
97
-                    styles.avatarContainer(this.props.size || DEFAULT_SIZE),
98
-                    this.props.style
99
-                ] }>
100
-                { avatar }
101
-            </View>
102
-        );
103
-    }
104
-}
105
-
106
-export default connect(_mapStateToProps)(Avatar);

+ 0
- 50
react/features/base/avatar/components/native/RemoteAvatar.js Ver fichero

@@ -1,50 +0,0 @@
1
-// @flow
2
-
3
-import React, { PureComponent } from 'react';
4
-import { Image } from 'react-native';
5
-
6
-import styles from './styles';
7
-
8
-export const DEFAULT_AVATAR = require('../../../../../../images/avatar.png');
9
-
10
-type Props = {
11
-
12
-    /**
13
-     * Callback for load errors.
14
-     */
15
-    onError: Function,
16
-
17
-    /**
18
-     * Size of the avatar.
19
-     */
20
-    size: number,
21
-
22
-    /**
23
-     * URI of the avatar to load.
24
-     */
25
-    uri: string
26
-};
27
-
28
-/**
29
- * Implements a private class that is used to fetch and render remote avatars based on an URI.
30
- */
31
-export default class RemoteAvatar extends PureComponent<Props> {
32
-
33
-    /**
34
-     * Implements {@code Component#render}.
35
-     *
36
-     * @inheritdoc
37
-     */
38
-    render() {
39
-        const { onError, size, uri } = this.props;
40
-
41
-        return (
42
-            <Image
43
-                defaultSource = { DEFAULT_AVATAR }
44
-                onError = { onError }
45
-                resizeMode = 'cover'
46
-                source = {{ uri }}
47
-                style = { styles.avatarContent(size) } />
48
-        );
49
-    }
50
-}

+ 112
- 0
react/features/base/avatar/components/native/StatelessAvatar.js Ver fichero

@@ -0,0 +1,112 @@
1
+// @flow
2
+
3
+import React from 'react';
4
+import { Image, Text, View } from 'react-native';
5
+
6
+import { type StyleType } from '../../../styles';
7
+
8
+import AbstractStatelessAvatar, { type Props as AbstractProps } from '../AbstractStatelessAvatar';
9
+
10
+import styles from './styles';
11
+
12
+type Props = AbstractProps & {
13
+
14
+    /**
15
+     * External style passed to the componant.
16
+     */
17
+    style?: StyleType
18
+};
19
+
20
+const DEFAULT_AVATAR = require('../../../../../../images/avatar.png');
21
+
22
+/**
23
+ * Implements a stateless avatar component that renders an avatar purely from what gets passed through
24
+ * props.
25
+ */
26
+export default class StatelessAvatar extends AbstractStatelessAvatar<Props> {
27
+    /**
28
+     * Implements {@code Component#render}.
29
+     *
30
+     * @inheritdoc
31
+     */
32
+    render() {
33
+        const { initials, size, style, url } = this.props;
34
+
35
+        let avatar;
36
+
37
+        if (url) {
38
+            avatar = this._renderURLAvatar();
39
+        } else if (initials) {
40
+            avatar = this._renderInitialsAvatar();
41
+        } else {
42
+            avatar = this._renderDefaultAvatar();
43
+        }
44
+
45
+        return (
46
+            <View
47
+                style = { [
48
+                    styles.avatarContainer(size),
49
+                    style
50
+                ] }>
51
+                { avatar }
52
+            </View>
53
+        );
54
+    }
55
+
56
+    /**
57
+     * Renders the default avatar.
58
+     *
59
+     * @returns {React$Element<*>}
60
+     */
61
+    _renderDefaultAvatar() {
62
+        const { size } = this.props;
63
+
64
+        return (
65
+            <Image
66
+                source = { DEFAULT_AVATAR }
67
+                style = { [
68
+                    styles.avatarContent(size),
69
+                    styles.staticAvatar
70
+                ] } />
71
+        );
72
+    }
73
+
74
+    /**
75
+     * Renders the initials-based avatar.
76
+     *
77
+     * @returns {React$Element<*>}
78
+     */
79
+    _renderInitialsAvatar() {
80
+        const { color, initials, size } = this.props;
81
+
82
+        return (
83
+            <View
84
+                style = { [
85
+                    styles.initialsContainer,
86
+                    {
87
+                        backgroundColor: color
88
+                    }
89
+                ] }>
90
+                <Text style = { styles.initialsText(size) }> { initials } </Text>
91
+            </View>
92
+        );
93
+    }
94
+
95
+    /**
96
+     * Renders the url-based avatar.
97
+     *
98
+     * @returns {React$Element<*>}
99
+     */
100
+    _renderURLAvatar() {
101
+        const { onAvatarLoadError, size, url } = this.props;
102
+
103
+        return (
104
+            <Image
105
+                defaultSource = { DEFAULT_AVATAR }
106
+                onError = { onAvatarLoadError }
107
+                resizeMode = 'cover'
108
+                source = {{ uri: url }}
109
+                style = { styles.avatarContent(size) } />
110
+        );
111
+    }
112
+}

+ 1
- 1
react/features/base/avatar/components/native/index.js Ver fichero

@@ -1,3 +1,3 @@
1 1
 // @flow
2 2
 
3
-export { default as Avatar } from './Avatar';
3
+export { default as StatelessAvatar } from './StatelessAvatar';

+ 5
- 3
react/features/base/avatar/components/native/styles.js Ver fichero

@@ -2,12 +2,14 @@
2 2
 
3 3
 import { ColorPalette } from '../../../styles';
4 4
 
5
+const DEFAULT_SIZE = 65;
6
+
5 7
 /**
6 8
  * The styles of the feature base/participants.
7 9
  */
8 10
 export default {
9 11
 
10
-    avatarContainer: (size: number) => {
12
+    avatarContainer: (size: number = DEFAULT_SIZE) => {
11 13
         return {
12 14
             alignItems: 'center',
13 15
             borderRadius: size / 2,
@@ -18,7 +20,7 @@ export default {
18 20
         };
19 21
     },
20 22
 
21
-    avatarContent: (size: number) => {
23
+    avatarContent: (size: number = DEFAULT_SIZE) => {
22 24
         return {
23 25
             height: size,
24 26
             width: size
@@ -32,7 +34,7 @@ export default {
32 34
         justifyContent: 'center'
33 35
     },
34 36
 
35
-    initialsText: (size: number) => {
37
+    initialsText: (size: number = DEFAULT_SIZE) => {
36 38
         return {
37 39
             color: 'rgba(255, 255, 255, 0.6)',
38 40
             fontSize: size * 0.5,

+ 0
- 98
react/features/base/avatar/components/web/Avatar.js Ver fichero

@@ -1,98 +0,0 @@
1
-// @flow
2
-
3
-import React from 'react';
4
-
5
-import { connect } from '../../../redux';
6
-
7
-import AbstractAvatar, {
8
-    _mapStateToProps,
9
-    type Props as AbstractProps
10
-} from '../AbstractAvatar';
11
-
12
-type Props = AbstractProps & {
13
-    className?: string,
14
-    id: string
15
-};
16
-
17
-/**
18
- * Implements an avatar as a React/Web {@link Component}.
19
- */
20
-class Avatar extends AbstractAvatar<Props> {
21
-    /**
22
-     * Constructs a style object to be used on the avatars.
23
-     *
24
-     * @param {string?} color - The desired background color.
25
-     * @returns {Object}
26
-     */
27
-    _getAvatarStyle(color) {
28
-        const { size } = this.props;
29
-
30
-        return {
31
-            backgroundColor: color || undefined,
32
-            fontSize: size ? size * 0.5 : '180%',
33
-            height: size || '100%',
34
-            width: size || '100%'
35
-        };
36
-    }
37
-
38
-    /**
39
-     * Constructs a list of class names required for the avatar component.
40
-     *
41
-     * @param {string} additional - Any additional class to add.
42
-     * @returns {string}
43
-     */
44
-    _getAvatarClassName(additional) {
45
-        return `avatar ${additional || ''} ${this.props.className || ''}`;
46
-    }
47
-
48
-    _onAvatarLoadError: () => void;
49
-
50
-    /**
51
-     * Implements {@code AbstractAvatar#_renderDefaultAvatar}.
52
-     *
53
-     * @inheritdoc
54
-     */
55
-    _renderDefaultAvatar() {
56
-        return (
57
-            <img
58
-                className = { this._getAvatarClassName('defaultAvatar') }
59
-                id = { this.props.id }
60
-                src = 'images/avatar.png'
61
-                style = { this._getAvatarStyle() } />
62
-        );
63
-    }
64
-
65
-    /**
66
-     * Implements {@code AbstractAvatar#_renderGravatar}.
67
-     *
68
-     * @inheritdoc
69
-     */
70
-    _renderInitialsAvatar(initials, color) {
71
-        return (
72
-            <div
73
-                className = { this._getAvatarClassName() }
74
-                id = { this.props.id }
75
-                style = { this._getAvatarStyle(color) }>
76
-                { initials }
77
-            </div>
78
-        );
79
-    }
80
-
81
-    /**
82
-     * Implements {@code AbstractAvatar#_renderGravatar}.
83
-     *
84
-     * @inheritdoc
85
-     */
86
-    _renderURLAvatar(uri) {
87
-        return (
88
-            <img
89
-                className = { this._getAvatarClassName() }
90
-                id = { this.props.id }
91
-                onError = { this._onAvatarLoadError }
92
-                src = { uri }
93
-                style = { this._getAvatarStyle() } />
94
-        );
95
-    }
96
-}
97
-
98
-export default connect(_mapStateToProps)(Avatar);

+ 96
- 0
react/features/base/avatar/components/web/StatelessAvatar.js Ver fichero

@@ -0,0 +1,96 @@
1
+// @flow
2
+
3
+import React from 'react';
4
+
5
+import AbstractStatelessAvatar, { type Props as AbstractProps } from '../AbstractStatelessAvatar';
6
+
7
+type Props = AbstractProps & {
8
+
9
+    /**
10
+     * External class name passed through props.
11
+     */
12
+    className?: string,
13
+
14
+    /**
15
+     * The default avatar URL if we want to override the app bundled one (e.g. AlwaysOnTop)
16
+     */
17
+    defaultAvatar?: string,
18
+
19
+    /**
20
+     * ID of the component to be rendered.
21
+     */
22
+    id?: string
23
+};
24
+
25
+/**
26
+ * Implements a stateless avatar component that renders an avatar purely from what gets passed through
27
+ * props.
28
+ */
29
+export default class StatelessAvatar extends AbstractStatelessAvatar<Props> {
30
+    /**
31
+     * Implements {@code Component#render}.
32
+     *
33
+     * @inheritdoc
34
+     */
35
+    render() {
36
+        const { initials, url } = this.props;
37
+
38
+        if (url) {
39
+            return (
40
+                <img
41
+                    className = { this._getAvatarClassName() }
42
+                    id = { this.props.id }
43
+                    onError = { this.props.onAvatarLoadError }
44
+                    src = { url }
45
+                    style = { this._getAvatarStyle() } />
46
+            );
47
+        }
48
+
49
+        if (initials) {
50
+            return (
51
+                <div
52
+                    className = { this._getAvatarClassName() }
53
+                    id = { this.props.id }
54
+                    style = { this._getAvatarStyle(this.props.color) }>
55
+                    { initials }
56
+                </div>
57
+            );
58
+        }
59
+
60
+        // default avatar
61
+        return (
62
+            <img
63
+                className = { this._getAvatarClassName('defaultAvatar') }
64
+                id = { this.props.id }
65
+                src = { this.props.defaultAvatar || 'images/avatar.png' }
66
+                style = { this._getAvatarStyle() } />
67
+        );
68
+    }
69
+
70
+    /**
71
+     * Constructs a style object to be used on the avatars.
72
+     *
73
+     * @param {string?} color - The desired background color.
74
+     * @returns {Object}
75
+     */
76
+    _getAvatarStyle(color) {
77
+        const { size } = this.props;
78
+
79
+        return {
80
+            backgroundColor: color || undefined,
81
+            fontSize: size ? size * 0.5 : '180%',
82
+            height: size || '100%',
83
+            width: size || '100%'
84
+        };
85
+    }
86
+
87
+    /**
88
+     * Constructs a list of class names required for the avatar component.
89
+     *
90
+     * @param {string} additional - Any additional class to add.
91
+     * @returns {string}
92
+     */
93
+    _getAvatarClassName(additional) {
94
+        return `avatar ${additional || ''} ${this.props.className || ''}`;
95
+    }
96
+}

+ 1
- 1
react/features/base/avatar/components/web/index.js Ver fichero

@@ -1,3 +1,3 @@
1 1
 // @flow
2 2
 
3
-export { default as Avatar } from './Avatar';
3
+export { default as StatelessAvatar } from './StatelessAvatar';

+ 1
- 0
react/features/base/avatar/index.js Ver fichero

@@ -1,3 +1,4 @@
1 1
 // @flow
2 2
 
3 3
 export * from './components';
4
+export * from './functions';

+ 1
- 1
react/features/base/react/components/native/AvatarListItem.js Ver fichero

@@ -91,7 +91,7 @@ export default class AvatarListItem extends Component<Props> {
91 91
                     displayName = { title }
92 92
                     size = { avatarSize }
93 93
                     style = { avatarStyle }
94
-                    uri = { avatar } />
94
+                    url = { avatar } />
95 95
                 <Container style = { styles.listItemDetails }>
96 96
                     <Text
97 97
                         numberOfLines = { 1 }

+ 19
- 0
react/features/base/util/helpers.js Ver fichero

@@ -18,6 +18,25 @@ export function createDeferred(): Object {
18 18
     return deferred;
19 19
 }
20 20
 
21
+/**
22
+ * Returns the base URL of the app.
23
+ *
24
+ * @param {Object} w - Window object to use instead of the built in one.
25
+ * @returns {string}
26
+ */
27
+export function getBaseUrl(w: Object = window) {
28
+    const doc = w.document;
29
+    const base = doc.querySelector('base');
30
+
31
+    if (base && base.href) {
32
+        return base.href;
33
+    }
34
+
35
+    const { protocol, host } = w.location;
36
+
37
+    return `${protocol}//${host}`;
38
+}
39
+
21 40
 /**
22 41
  * Returns the namespace for all global variables, functions, etc that we need.
23 42
  *

+ 32
- 8
react/features/external-api/middleware.js Ver fichero

@@ -10,11 +10,11 @@ import { JitsiConferenceErrors } from '../base/lib-jitsi-meet';
10 10
 import {
11 11
     PARTICIPANT_KICKED,
12 12
     SET_LOADABLE_AVATAR_URL,
13
-    getAvatarURLByParticipantId,
14 13
     getLocalParticipant,
15 14
     getParticipantById
16 15
 } from '../base/participants';
17 16
 import { MiddlewareRegistry } from '../base/redux';
17
+import { getBaseUrl } from '../base/util';
18 18
 import { appendSuffix } from '../display-name';
19 19
 import { SUBMIT_FEEDBACK } from '../feedback';
20 20
 import { SET_FILMSTRIP_VISIBLE } from '../filmstrip';
@@ -39,11 +39,26 @@ MiddlewareRegistry.register(store => next => action => {
39 39
 
40 40
         const result = next(action);
41 41
 
42
-        if (participant && (participant.loadableAvatarUrl !== loadableAvatarUrl)) {
43
-            APP.API.notifyAvatarChanged(
44
-                id,
45
-                loadableAvatarUrl
46
-            );
42
+        if (participant) {
43
+            if (loadableAvatarUrl) {
44
+                participant.loadableAvatarUrl !== loadableAvatarUrl && APP.API.notifyAvatarChanged(
45
+                    id,
46
+                    loadableAvatarUrl
47
+                );
48
+            } else {
49
+                // There is no loadable explicit URL. In this case the Avatar component would
50
+                // decide to render initials or the default avatar, but the external API needs
51
+                // a URL when it needs to be rendered, so if there is no initials, we return the default
52
+                // Avatar URL as if it was a usual avatar URL. If there are (or may be) initials
53
+                // we send undefined to signal the api user that it's not an URL that needs to be rendered.
54
+                //
55
+                // NOTE: we may implement a special URL format later to signal that the avatar is based
56
+                // on initials, that API consumers can handle as they want, e.g. initials://jm
57
+                APP.API.notifyAvatarChanged(
58
+                    id,
59
+                    participant.name ? undefined : _getDefaultAvatarUrl()
60
+                );
61
+            }
47 62
         }
48 63
 
49 64
         return result;
@@ -65,7 +80,7 @@ MiddlewareRegistry.register(store => next => action => {
65 80
     case CONFERENCE_JOINED: {
66 81
         const state = store.getState();
67 82
         const { room } = state['features/base/conference'];
68
-        const { name, id } = getLocalParticipant(state);
83
+        const { loadableAvatarUrl, name, id } = getLocalParticipant(state);
69 84
 
70 85
         APP.API.notifyConferenceJoined(
71 86
             room,
@@ -76,7 +91,7 @@ MiddlewareRegistry.register(store => next => action => {
76 91
                     name,
77 92
                     interfaceConfig.DEFAULT_LOCAL_DISPLAY_NAME
78 93
                 ),
79
-                avatarURL: getAvatarURLByParticipantId(state, id)
94
+                avatarURL: loadableAvatarUrl
80 95
             }
81 96
         );
82 97
         break;
@@ -125,3 +140,12 @@ MiddlewareRegistry.register(store => next => action => {
125 140
 
126 141
     return result;
127 142
 });
143
+
144
+/**
145
+ * Returns the absolute URL of the default avatar.
146
+ *
147
+ * @returns {string}
148
+ */
149
+function _getDefaultAvatarUrl() {
150
+    return new URL('images/avatar.png', getBaseUrl()).href;
151
+}

+ 1
- 1
react/features/mobile/incoming-call/components/IncomingCallPage.js Ver fichero

@@ -130,7 +130,7 @@ class IncomingCallPage extends Component<Props> {
130 130
                 <View style = { styles.avatar }>
131 131
                     <Avatar
132 132
                         size = { CALLER_AVATAR_SIZE }
133
-                        uri = { this.props._callerAvatarURL } />
133
+                        url = { this.props._callerAvatarURL } />
134 134
                 </View>
135 135
             </View>
136 136
         );

Loading…
Cancelar
Guardar