浏览代码

ref(avatars): remove Avatar.js (#2289)

* ref(avatars): remove Avatar.js

- Rely on redux getting updated with new participant state
and any calls to getAvatarURL passing in the redux
participant state. This way the state within Avatar.js can
be removed.
- Clean up methods on UI.js. Because all state is in the
store, separate methods for updating the avatar aren't as
necessary. Instead centralize accessing of the avatar for
components outside of redux and centralize the call to
update avatars for non-react components.
- Controversial: cache a participant's avatarURL on the
participant state. Currently the participant's avatarURL
that is generated without jwt (which sets the avatarURL directly)
is not cached. Without cache, there can be many redundant
calls to APP.API.notifyAvatarChanged.

* Leverage middleware timing to diff avatars

One alternative implementation is to leverage middleware's
ability to intercept updates before and after redux has
upated and then compare avatarURLs.

* kill UI.getAvatarUrl

* profile button sets its own avatar url (solves update timing)

* remove calls to updating avatar outside of middleware

* update UI.js doc

* remove left over logic from initial implementation

* try to move local user fallback into selector func

* default to id 'local' in selector
master
virtuacoplenny 7 年前
父节点
当前提交
28013f6ffa

+ 4
- 5
conference.js 查看文件

70
 } from './react/features/base/media';
70
 } from './react/features/base/media';
71
 import {
71
 import {
72
     dominantSpeakerChanged,
72
     dominantSpeakerChanged,
73
+    getAvatarURLByParticipantId,
73
     getLocalParticipant,
74
     getLocalParticipant,
74
     getParticipantById,
75
     getParticipantById,
75
     localParticipantConnectionStatusChanged,
76
     localParticipantConnectionStatusChanged,
2092
                         id: from,
2093
                         id: from,
2093
                         avatarURL: data.value
2094
                         avatarURL: data.value
2094
                     }));
2095
                     }));
2095
-                APP.UI.setUserAvatarUrl(from, data.value);
2096
             });
2096
             });
2097
 
2097
 
2098
         room.addCommandListener(this.commands.defaults.AVATAR_ID,
2098
         room.addCommandListener(this.commands.defaults.AVATAR_ID,
2102
                         id: from,
2102
                         id: from,
2103
                         avatarID: data.value
2103
                         avatarID: data.value
2104
                     }));
2104
                     }));
2105
-                APP.UI.setUserAvatarID(from, data.value);
2106
             });
2105
             });
2107
 
2106
 
2108
         APP.UI.addListener(UIEvents.NICKNAME_CHANGED,
2107
         APP.UI.addListener(UIEvents.NICKNAME_CHANGED,
2414
                 formattedDisplayName: appendSuffix(
2413
                 formattedDisplayName: appendSuffix(
2415
                     displayName,
2414
                     displayName,
2416
                     interfaceConfig.DEFAULT_LOCAL_DISPLAY_NAME),
2415
                     interfaceConfig.DEFAULT_LOCAL_DISPLAY_NAME),
2417
-                avatarURL: APP.UI.getAvatarUrl()
2416
+                avatarURL: getAvatarURLByParticipantId(
2417
+                    APP.store.getState(), this._room.myUserId())
2418
             }
2418
             }
2419
         );
2419
         );
2420
         APP.UI.markVideoInterrupted(false);
2420
         APP.UI.markVideoInterrupted(false);
2704
         APP.store.dispatch(participantUpdated({
2704
         APP.store.dispatch(participantUpdated({
2705
             id: localId,
2705
             id: localId,
2706
             local: true,
2706
             local: true,
2707
-            formattedEmail
2707
+            email: formattedEmail
2708
         }));
2708
         }));
2709
 
2709
 
2710
         APP.settings.setEmail(formattedEmail);
2710
         APP.settings.setEmail(formattedEmail);
2732
         }));
2732
         }));
2733
 
2733
 
2734
         APP.settings.setAvatarUrl(url);
2734
         APP.settings.setAvatarUrl(url);
2735
-        APP.UI.setUserAvatarUrl(id, url);
2736
         sendData(commands.AVATAR_URL, url);
2735
         sendData(commands.AVATAR_URL, url);
2737
     },
2736
     },
2738
 
2737
 

+ 8
- 50
modules/UI/UI.js 查看文件

6
 
6
 
7
 import Chat from './side_pannels/chat/Chat';
7
 import Chat from './side_pannels/chat/Chat';
8
 import SidePanels from './side_pannels/SidePanels';
8
 import SidePanels from './side_pannels/SidePanels';
9
-import Avatar from './avatar/Avatar';
10
 import SideContainerToggler from './side_pannels/SideContainerToggler';
9
 import SideContainerToggler from './side_pannels/SideContainerToggler';
11
 import messageHandler from './util/MessageHandler';
10
 import messageHandler from './util/MessageHandler';
12
 import UIUtil from './util/UIUtil';
11
 import UIUtil from './util/UIUtil';
255
  */
254
  */
256
 UI.initConference = function() {
255
 UI.initConference = function() {
257
     const { dispatch, getState } = APP.store;
256
     const { dispatch, getState } = APP.store;
258
-    const { avatarID, email, id, name } = getLocalParticipant(getState);
257
+    const { email, id, name } = getLocalParticipant(getState);
259
 
258
 
260
     // Update default button states before showing the toolbar
259
     // Update default button states before showing the toolbar
261
     // if local role changes buttons state will be again updated.
260
     // if local role changes buttons state will be again updated.
272
     // Make sure we configure our avatar id, before creating avatar for us
271
     // Make sure we configure our avatar id, before creating avatar for us
273
     if (email) {
272
     if (email) {
274
         UI.setUserEmail(id, email);
273
         UI.setUserEmail(id, email);
275
-    } else {
276
-        UI.setUserAvatarID(id, avatarID);
277
     }
274
     }
278
 
275
 
279
     dispatch(checkAutoEnableDesktopSharing());
276
     dispatch(checkAutoEnableDesktopSharing());
789
 // Used by torture.
786
 // Used by torture.
790
 UI.dockToolbar = dock => APP.store.dispatch(dockToolbox(dock));
787
 UI.dockToolbar = dock => APP.store.dispatch(dockToolbox(dock));
791
 
788
 
792
-/**
793
- * Updates the avatar for participant.
794
- * @param {string} id user id
795
- * @param {string} avatarUrl the URL for the avatar
796
- */
797
-function changeAvatar(id, avatarUrl) {
798
-    VideoLayout.changeUserAvatar(id, avatarUrl);
799
-    if (APP.conference.isLocalId(id)) {
800
-        Profile.changeAvatar(avatarUrl);
801
-    }
802
-}
803
-
804
-/**
805
- * Returns the avatar URL for a given user.
806
- *
807
- * @param {string} id - The id of the user.
808
- * @returns {string} The avatar URL.
809
- */
810
-UI.getAvatarUrl = function(id) {
811
-    return Avatar.getAvatarUrl(id);
812
-};
813
-
814
 /**
789
 /**
815
  * Update user email.
790
  * Update user email.
816
  * @param {string} id user id
791
  * @param {string} id user id
817
  * @param {string} email user email
792
  * @param {string} email user email
818
  */
793
  */
819
 UI.setUserEmail = function(id, email) {
794
 UI.setUserEmail = function(id, email) {
820
-    // update avatar
821
-    Avatar.setUserEmail(id, email);
822
-
823
-    changeAvatar(id, Avatar.getAvatarUrl(id));
824
     if (APP.conference.isLocalId(id)) {
795
     if (APP.conference.isLocalId(id)) {
825
         Profile.changeEmail(email);
796
         Profile.changeEmail(email);
826
     }
797
     }
827
 };
798
 };
828
 
799
 
829
 /**
800
 /**
830
- * Update user avtar id.
831
- * @param {string} id user id
832
- * @param {string} avatarId user's avatar id
833
- */
834
-UI.setUserAvatarID = function(id, avatarId) {
835
-    // update avatar
836
-    Avatar.setUserAvatarID(id, avatarId);
837
-
838
-    changeAvatar(id, Avatar.getAvatarUrl(id));
839
-};
840
-
841
-/**
842
- * Update user avatar URL.
843
- * @param {string} id user id
844
- * @param {string} url user avatar url
801
+ * Updates the displayed avatar for participant.
802
+ *
803
+ * @param {string} id - User id whose avatar should be updated.
804
+ * @param {string} avatarURL - The URL to avatar image to display.
805
+ * @returns {void}
845
  */
806
  */
846
-UI.setUserAvatarUrl = function(id, url) {
847
-    // update avatar
848
-    Avatar.setUserAvatarUrl(id, url);
849
-
850
-    changeAvatar(id, Avatar.getAvatarUrl(id));
807
+UI.refreshAvatarDisplay = function(id, avatarURL) {
808
+    VideoLayout.changeUserAvatar(id, avatarURL);
851
 };
809
 };
852
 
810
 
853
 /**
811
 /**

+ 0
- 109
modules/UI/avatar/Avatar.js 查看文件

1
-/*
2
- * Adorable Avatars service used at the end of this file is released under the
3
- * terms of the MIT License.
4
- *
5
- * Copyright (c) 2014 Adorable IO LLC
6
- * Permission is hereby granted, free of charge, to any person obtaining a copy
7
- * of this software and associated documentation files (the "Software"), to deal
8
- * in the Software without restriction, including without limitation the rights
9
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
- * copies of the Software, and to permit persons to whom the Software is
11
- * furnished to do so, subject to the following conditions:
12
- * The above copyright notice and this permission notice shall be included in
13
- * all copies or substantial portions of the Software.
14
- *
15
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- * SOFTWARE.
22
- */
23
-
24
-/* global APP */
25
-
26
-import { getAvatarURL } from '../../../react/features/base/participants';
27
-
28
-const users = {};
29
-
30
-export default {
31
-    /**
32
-     * Sets prop in users object.
33
-     * @param id {string} user id or undefined for the local user.
34
-     * @param prop {string} name of the prop
35
-     * @param val {string} value to be set
36
-     */
37
-    _setUserProp(id, prop, val) {
38
-        // FIXME: Fixes the issue with not be able to return avatar for the
39
-        // local user when the conference has been left. Maybe there is beter
40
-        // way to solve it.
41
-        if (!id || APP.conference.isLocalId(id)) {
42
-            id = 'local';// eslint-disable-line no-param-reassign
43
-        }
44
-        if (!val || (users[id] && users[id][prop] === val)) {
45
-            return;
46
-        }
47
-        if (!users[id]) {
48
-            users[id] = {};
49
-        }
50
-        users[id][prop] = val;
51
-        APP.API.notifyAvatarChanged(
52
-            id === 'local' ? APP.conference.getMyUserId() : id,
53
-            this.getAvatarUrl(id)
54
-        );
55
-    },
56
-
57
-    /**
58
-     * Sets the user's avatar in the settings menu(if local user), contact list
59
-     * and thumbnail
60
-     * @param id id of the user
61
-     * @param email email or nickname to be used as a hash
62
-     */
63
-    setUserEmail(id, email) {
64
-        this._setUserProp(id, 'email', email);
65
-    },
66
-
67
-    /**
68
-     * Sets the user's avatar in the settings menu(if local user), contact list
69
-     * and thumbnail
70
-     * @param id id of the user
71
-     * @param url the url for the avatar
72
-     */
73
-    setUserAvatarUrl(id, url) {
74
-        this._setUserProp(id, 'avatarUrl', url);
75
-    },
76
-
77
-    /**
78
-     * Sets the user's avatar id.
79
-     * @param id id of the user
80
-     * @param avatarId an id to be used for the avatar
81
-     */
82
-    setUserAvatarID(id, avatarId) {
83
-        this._setUserProp(id, 'avatarId', avatarId);
84
-    },
85
-
86
-    /**
87
-     * Returns the URL of the image for the avatar of a particular user,
88
-     * identified by its id.
89
-     * @param {string} userId user id
90
-     */
91
-    getAvatarUrl(userId) {
92
-        let user;
93
-
94
-        if (!userId || APP.conference.isLocalId(userId)) {
95
-            user = users.local;
96
-            // eslint-disable-next-line no-param-reassign
97
-            userId = APP.conference.getMyUserId();
98
-        } else {
99
-            user = users[userId];
100
-        }
101
-
102
-        return getAvatarURL({
103
-            avatarID: user ? user.avatarId : undefined,
104
-            avatarURL: user ? user.avatarUrl : undefined,
105
-            email: user ? user.email : undefined,
106
-            id: userId
107
-        });
108
-    }
109
-};

+ 0
- 8
modules/UI/side_pannels/profile/Profile.js 查看文件

142
         $('#setDisplayName').val(newDisplayName);
142
         $('#setDisplayName').val(newDisplayName);
143
     },
143
     },
144
 
144
 
145
-    /**
146
-     * Change user avatar in the settings menu.
147
-     * @param {string} avatarUrl url of the new avatar
148
-     */
149
-    changeAvatar(avatarUrl) {
150
-        $('#avatar').attr('src', avatarUrl);
151
-    },
152
-
153
     /**
145
     /**
154
      * Change the value of the field for the user email.
146
      * Change the value of the field for the user email.
155
      * @param {string} email the new value that will be displayed in the field.
147
      * @param {string} email the new value that will be displayed in the field.

+ 5
- 3
modules/UI/videolayout/LargeVideoManager.js 查看文件

12
 import {
12
 import {
13
     JitsiParticipantConnectionStatus
13
     JitsiParticipantConnectionStatus
14
 } from '../../../react/features/base/lib-jitsi-meet';
14
 } from '../../../react/features/base/lib-jitsi-meet';
15
+import {
16
+    getAvatarURLByParticipantId
17
+} from '../../../react/features/base/participants';
15
 import {
18
 import {
16
     updateKnownLargeVideoResolution
19
     updateKnownLargeVideoResolution
17
 } from '../../../react/features/large-video';
20
 } from '../../../react/features/large-video';
18
-
19
-import Avatar from '../avatar/Avatar';
20
 import { createDeferred } from '../../util/helpers';
21
 import { createDeferred } from '../../util/helpers';
21
 import UIEvents from '../../../service/UI/UIEvents';
22
 import UIEvents from '../../../service/UI/UIEvents';
22
 import UIUtil from '../util/UIUtil';
23
 import UIUtil from '../util/UIUtil';
219
             container.setStream(id, stream, videoType);
220
             container.setStream(id, stream, videoType);
220
 
221
 
221
             // change the avatar url on large
222
             // change the avatar url on large
222
-            this.updateAvatar(Avatar.getAvatarUrl(id));
223
+            this.updateAvatar(
224
+                getAvatarURLByParticipantId(APP.store.getState(), id));
223
 
225
 
224
             // If the user's connection is disrupted then the avatar will be
226
             // If the user's connection is disrupted then the avatar will be
225
             // displayed in case we have no video image cached. That is if
227
             // displayed in case we have no video image cached. That is if

+ 9
- 0
modules/UI/videolayout/LocalVideo.js 查看文件

7
 
7
 
8
 import { JitsiTrackEvents } from '../../../react/features/base/lib-jitsi-meet';
8
 import { JitsiTrackEvents } from '../../../react/features/base/lib-jitsi-meet';
9
 import { VideoTrack } from '../../../react/features/base/media';
9
 import { VideoTrack } from '../../../react/features/base/media';
10
+import {
11
+    getAvatarURLByParticipantId
12
+} from '../../../react/features/base/participants';
10
 /* eslint-enable no-unused-vars */
13
 /* eslint-enable no-unused-vars */
11
 
14
 
12
 const logger = require('jitsi-meet-logger').getLogger(__filename);
15
 const logger = require('jitsi-meet-logger').getLogger(__filename);
46
     // Set default display name.
49
     // Set default display name.
47
     this.setDisplayName();
50
     this.setDisplayName();
48
 
51
 
52
+    // Initialize the avatar display with an avatar url selected from the redux
53
+    // state. Redux stores the local user with a hardcoded participant id of
54
+    // 'local' if no id has been assigned yet.
55
+    this.avatarChanged(
56
+        getAvatarURLByParticipantId(APP.store.getState(), this.id));
57
+
49
     this.addAudioLevelIndicator();
58
     this.addAudioLevelIndicator();
50
     this.updateIndicators();
59
     this.updateIndicators();
51
 
60
 

+ 4
- 3
modules/UI/videolayout/SmallVideo.js 查看文件

11
 import { AudioLevelIndicator }
11
 import { AudioLevelIndicator }
12
     from '../../../react/features/audio-level-indicator';
12
     from '../../../react/features/audio-level-indicator';
13
 import {
13
 import {
14
-    Avatar as AvatarDisplay
14
+    Avatar as AvatarDisplay,
15
+    getAvatarURLByParticipantId
15
 } from '../../../react/features/base/participants';
16
 } from '../../../react/features/base/participants';
16
 import {
17
 import {
17
     ConnectionIndicator
18
     ConnectionIndicator
28
 
29
 
29
 const logger = require('jitsi-meet-logger').getLogger(__filename);
30
 const logger = require('jitsi-meet-logger').getLogger(__filename);
30
 
31
 
31
-import Avatar from '../avatar/Avatar';
32
 import UIUtil from '../util/UIUtil';
32
 import UIUtil from '../util/UIUtil';
33
 import UIEvents from '../../../service/UI/UIEvents';
33
 import UIEvents from '../../../service/UI/UIEvents';
34
 
34
 
590
     if (!this.hasAvatar) {
590
     if (!this.hasAvatar) {
591
         if (this.id) {
591
         if (this.id) {
592
             // Init avatar
592
             // Init avatar
593
-            this.avatarChanged(Avatar.getAvatarUrl(this.id));
593
+            this.avatarChanged(
594
+                getAvatarURLByParticipantId(APP.store.getState(), this.id));
594
         } else {
595
         } else {
595
             logger.error('Unable to init avatar - no id', this);
596
             logger.error('Unable to init avatar - no id', this);
596
 
597
 

+ 27
- 1
react/features/base/participants/functions.js 查看文件

3
 
3
 
4
 import { toState } from '../redux';
4
 import { toState } from '../redux';
5
 
5
 
6
-import { DEFAULT_AVATAR_RELATIVE_PATH } from './constants';
6
+import {
7
+    DEFAULT_AVATAR_RELATIVE_PATH,
8
+    LOCAL_PARTICIPANT_DEFAULT_ID
9
+} from './constants';
7
 
10
 
8
 declare var config: Object;
11
 declare var config: Object;
9
 declare var interfaceConfig: Object;
12
 declare var interfaceConfig: Object;
74
     return urlPrefix + md5.hex(key.trim().toLowerCase()) + urlSuffix;
77
     return urlPrefix + md5.hex(key.trim().toLowerCase()) + urlSuffix;
75
 }
78
 }
76
 
79
 
80
+/**
81
+ * Returns the avatarURL for the participant associated with the passed in
82
+ * participant ID.
83
+ *
84
+ * @param {(Function|Object|Participant[])} stateful - The redux state
85
+ * features/base/participants, the (whole) redux state, or redux's
86
+ * {@code getState} function to be used to retrieve the state
87
+ * features/base/participants.
88
+ * @param {string} id - The ID of the participant to retrieve.
89
+ * @param {boolean} isLocal - An optional parameter indicating whether or not
90
+ * the partcipant id is for the local user. If true, a different logic flow is
91
+ * used find the local user, ignoring the id value as it can change through the
92
+ * beginning and end of a call.
93
+ * @returns {(string|undefined)}
94
+ */
95
+export function getAvatarURLByParticipantId(
96
+        stateful: Object | Function,
97
+        id: string = LOCAL_PARTICIPANT_DEFAULT_ID) {
98
+    const participant = getParticipantById(stateful, id);
99
+
100
+    return participant && getAvatarURL(participant);
101
+}
102
+
77
 /**
103
 /**
78
  * Returns local participant from Redux state.
104
  * Returns local participant from Redux state.
79
  *
105
  *

+ 39
- 2
react/features/base/participants/middleware.js 查看文件

12
 import {
12
 import {
13
     KICK_PARTICIPANT,
13
     KICK_PARTICIPANT,
14
     MUTE_REMOTE_PARTICIPANT,
14
     MUTE_REMOTE_PARTICIPANT,
15
-    PARTICIPANT_DISPLAY_NAME_CHANGED
15
+    PARTICIPANT_DISPLAY_NAME_CHANGED,
16
+    PARTICIPANT_JOINED,
17
+    PARTICIPANT_UPDATED
16
 } from './actionTypes';
18
 } from './actionTypes';
17
 import { LOCAL_PARTICIPANT_DEFAULT_ID } from './constants';
19
 import { LOCAL_PARTICIPANT_DEFAULT_ID } from './constants';
18
-import { getLocalParticipant } from './functions';
20
+import {
21
+    getAvatarURLByParticipantId,
22
+    getLocalParticipant
23
+} from './functions';
19
 
24
 
20
 declare var APP: Object;
25
 declare var APP: Object;
21
 
26
 
59
 
64
 
60
         break;
65
         break;
61
     }
66
     }
67
+
68
+    case PARTICIPANT_JOINED:
69
+    case PARTICIPANT_UPDATED: {
70
+        if (typeof APP !== 'undefined') {
71
+            const participant = action.participant;
72
+            const { id, local } = participant;
73
+
74
+            const preUpdateAvatarURL
75
+                = getAvatarURLByParticipantId(store.getState(), id);
76
+
77
+            // Allow the redux update to go through and compare the old avatar
78
+            // to the new avatar and emit out change events if necessary.
79
+            const result = next(action);
80
+
81
+            const postUpdateAvatarURL
82
+                = getAvatarURLByParticipantId(store.getState(), id);
83
+
84
+            if (preUpdateAvatarURL !== postUpdateAvatarURL) {
85
+                const currentKnownId = local
86
+                    ? APP.conference.getMyUserId() : id;
87
+
88
+                APP.UI.refreshAvatarDisplay(
89
+                    currentKnownId, postUpdateAvatarURL);
90
+                APP.API.notifyAvatarChanged(
91
+                    currentKnownId, postUpdateAvatarURL);
92
+            }
93
+
94
+            return result;
95
+        }
96
+
97
+        break;
98
+    }
62
     }
99
     }
63
 
100
 
64
     return next(action);
101
     return next(action);

+ 18
- 3
react/features/toolbox/components/ProfileButton.web.js 查看文件

5
 import { connect } from 'react-redux';
5
 import { connect } from 'react-redux';
6
 
6
 
7
 import { TOOLBAR_PROFILE_TOGGLED, sendAnalyticsEvent } from '../../analytics';
7
 import { TOOLBAR_PROFILE_TOGGLED, sendAnalyticsEvent } from '../../analytics';
8
-import { DEFAULT_AVATAR_RELATIVE_PATH } from '../../base/participants';
8
+import {
9
+    getAvatarURL,
10
+    getLocalParticipant
11
+} from '../../base/participants';
9
 import UIEvents from '../../../../service/UI/UIEvents';
12
 import UIEvents from '../../../../service/UI/UIEvents';
10
 
13
 
11
 import ToolbarButton from './ToolbarButton';
14
 import ToolbarButton from './ToolbarButton';
39
      * @static
42
      * @static
40
      */
43
      */
41
     static propTypes = {
44
     static propTypes = {
45
+        /**
46
+         * The redux representation of the local participant.
47
+         */
48
+        _localParticipant: PropTypes.object,
49
+
42
         /**
50
         /**
43
          * Whether the button support clicking or not.
51
          * Whether the button support clicking or not.
44
          */
52
          */
76
      * @returns {ReactElement}
84
      * @returns {ReactElement}
77
      */
85
      */
78
     render() {
86
     render() {
79
-        const { _unclickable, tooltipPosition, toggled } = this.props;
87
+        const {
88
+            _localParticipant,
89
+            _unclickable,
90
+            tooltipPosition,
91
+            toggled
92
+        } = this.props;
80
         const buttonConfiguration = {
93
         const buttonConfiguration = {
81
             ...DEFAULT_BUTTON_CONFIGURATION,
94
             ...DEFAULT_BUTTON_CONFIGURATION,
82
             unclickable: _unclickable,
95
             unclickable: _unclickable,
90
                 tooltipPosition = { tooltipPosition }>
103
                 tooltipPosition = { tooltipPosition }>
91
                 <img
104
                 <img
92
                     id = 'avatar'
105
                     id = 'avatar'
93
-                    src = { DEFAULT_AVATAR_RELATIVE_PATH } />
106
+                    src = { getAvatarURL(_localParticipant) } />
94
             </ToolbarButton>
107
             </ToolbarButton>
95
         );
108
         );
96
     }
109
     }
115
  * @param {Object} state - The Redux state.
128
  * @param {Object} state - The Redux state.
116
  * @private
129
  * @private
117
  * @returns {{
130
  * @returns {{
131
+ *     _localParticipant: Object,
118
  *     _unclickable: boolean
132
  *     _unclickable: boolean
119
  * }}
133
  * }}
120
  */
134
  */
121
 function _mapStateToProps(state) {
135
 function _mapStateToProps(state) {
122
     return {
136
     return {
137
+        _localParticipant: getLocalParticipant(state),
123
         _unclickable: !state['features/base/jwt'].isGuest
138
         _unclickable: !state['features/base/jwt'].isGuest
124
     };
139
     };
125
 }
140
 }

正在加载...
取消
保存