瀏覽代碼

feat: auto tile view

j8
Bettenbuk Zoltan 4 年之前
父節點
當前提交
240b033e76

+ 2
- 1
modules/UI/shared_video/SharedVideo.js 查看文件

@@ -17,6 +17,7 @@ import {
17 17
     getToolboxHeight,
18 18
     showToolbox
19 19
 } from '../../../react/features/toolbox';
20
+import { YOUTUBE_PARTICIPANT_NAME } from '../../../react/features/youtube-player';
20 21
 import UIEvents from '../../../service/UI/UIEvents';
21 22
 import UIUtil from '../util/UIUtil';
22 23
 import Filmstrip from '../videolayout/Filmstrip';
@@ -305,7 +306,7 @@ export default class SharedVideoManager {
305 306
                 conference: APP.conference._room,
306 307
                 id: self.url,
307 308
                 isFakeParticipant: true,
308
-                name: 'YouTube'
309
+                name: YOUTUBE_PARTICIPANT_NAME
309 310
             }));
310 311
 
311 312
             APP.store.dispatch(pinParticipant(self.url));

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

@@ -451,7 +451,7 @@ export default class SmallVideo {
451 451
      */
452 452
     selectDisplayMode(input) {
453 453
         // Display name is always and only displayed when user is on the stage
454
-        if (input.isCurrentlyOnLargeVideo && !input.tileViewEnabled) {
454
+        if (input.isCurrentlyOnLargeVideo && !input.tileViewActive) {
455 455
             return input.isVideoPlayable && !input.isAudioOnly ? DISPLAY_BLACKNESS_WITH_NAME : DISPLAY_AVATAR_WITH_NAME;
456 456
         } else if (input.isVideoPlayable && input.hasVideo && !input.isAudioOnly) {
457 457
             // check hovering and change state to video with name
@@ -472,7 +472,7 @@ export default class SmallVideo {
472 472
             isCurrentlyOnLargeVideo: this.isCurrentlyOnLargeVideo(),
473 473
             isHovered: this._isHovered(),
474 474
             isAudioOnly: APP.conference.isAudioOnly(),
475
-            tileViewEnabled: shouldDisplayTileView(APP.store.getState()),
475
+            tileViewActive: shouldDisplayTileView(APP.store.getState()),
476 476
             isVideoPlayable: this.isVideoPlayable(),
477 477
             hasVideo: Boolean(this.selectVideoElement().length),
478 478
             connectionStatus: APP.conference.getParticipantConnectionStatus(this.id),

+ 3
- 1
react/features/base/lastn/middleware.js 查看文件

@@ -4,6 +4,7 @@ import { SET_FILMSTRIP_ENABLED } from '../../filmstrip/actionTypes';
4 4
 import { SELECT_LARGE_VIDEO_PARTICIPANT } from '../../large-video/actionTypes';
5 5
 import { APP_STATE_CHANGED } from '../../mobile/background/actionTypes';
6 6
 import { SCREEN_SHARE_PARTICIPANTS_UPDATED, SET_TILE_VIEW } from '../../video-layout/actionTypes';
7
+import { shouldDisplayTileView } from '../../video-layout/functions';
7 8
 import { SET_AUDIO_ONLY } from '../audio-only/actionTypes';
8 9
 import { CONFERENCE_JOINED } from '../conference/actionTypes';
9 10
 import { getParticipantById } from '../participants/functions';
@@ -59,7 +60,8 @@ function _updateLastN({ getState }) {
59 60
     if (typeof appState !== 'undefined' && appState !== 'active') {
60 61
         lastN = 0;
61 62
     } else if (audioOnly) {
62
-        const { screenShares, tileViewEnabled } = state['features/video-layout'];
63
+        const { screenShares } = state['features/video-layout'];
64
+        const tileViewEnabled = shouldDisplayTileView(state);
63 65
         const largeVideoParticipantId = state['features/large-video'].participantId;
64 66
         const largeVideoParticipant
65 67
             = largeVideoParticipantId ? getParticipantById(state, largeVideoParticipantId) : undefined;

+ 2
- 1
react/features/conference/components/web/InviteMore.js 查看文件

@@ -11,6 +11,7 @@ import {
11 11
     isButtonEnabled,
12 12
     isToolboxVisible
13 13
 } from '../../../toolbox';
14
+import { shouldDisplayTileView } from '../../../video-layout/functions';
14 15
 
15 16
 declare var interfaceConfig: Object;
16 17
 
@@ -83,7 +84,7 @@ function mapStateToProps(state) {
83 84
     const hide = interfaceConfig.HIDE_INVITE_MORE_HEADER;
84 85
 
85 86
     return {
86
-        _tileViewEnabled: state['features/video-layout'].tileViewEnabled,
87
+        _tileViewEnabled: shouldDisplayTileView(state),
87 88
         _visible: isToolboxVisible(state) && isButtonEnabled('invite') && isAlone && !hide
88 89
     };
89 90
 }

+ 3
- 6
react/features/follow-me/middleware.js 查看文件

@@ -152,17 +152,14 @@ function _onFollowMeCommand(attributes = {}, id, store) {
152 152
         }
153 153
     }
154 154
 
155
-    const pinnedParticipant
156
-        = getPinnedParticipant(state, attributes.nextOnStage);
155
+    const pinnedParticipant = getPinnedParticipant(state);
157 156
     const idOfParticipantToPin = attributes.nextOnStage;
158 157
 
159 158
     if (typeof idOfParticipantToPin !== 'undefined'
160
-            && (!pinnedParticipant
161
-                || idOfParticipantToPin !== pinnedParticipant.id)
159
+            && (!pinnedParticipant || idOfParticipantToPin !== pinnedParticipant.id)
162 160
             && oldState.nextOnStage !== attributes.nextOnStage) {
163 161
         _pinVideoThumbnailById(store, idOfParticipantToPin);
164
-    } else if (typeof idOfParticipantToPin === 'undefined'
165
-            && pinnedParticipant) {
162
+    } else if (typeof idOfParticipantToPin === 'undefined' && pinnedParticipant) {
166 163
         store.dispatch(pinParticipant(null));
167 164
     }
168 165
 }

+ 2
- 1
react/features/follow-me/subscriber.js 查看文件

@@ -6,6 +6,7 @@ import {
6 6
     isLocalParticipantModerator
7 7
 } from '../base/participants';
8 8
 import { StateListenerRegistry } from '../base/redux';
9
+import { shouldDisplayTileView } from '../video-layout/functions';
9 10
 
10 11
 import { FOLLOW_ME_COMMAND } from './constants';
11 12
 
@@ -72,7 +73,7 @@ function _getFollowMeState(state) {
72 73
         filmstripVisible: state['features/filmstrip'].visible,
73 74
         nextOnStage: pinnedParticipant && pinnedParticipant.id,
74 75
         sharedDocumentVisible: state['features/etherpad'].editing,
75
-        tileViewEnabled: state['features/video-layout'].tileViewEnabled
76
+        tileViewEnabled: shouldDisplayTileView(state)
76 77
     };
77 78
 }
78 79
 

+ 2
- 1
react/features/remote-video-menu/components/native/PinButton.js 查看文件

@@ -5,6 +5,7 @@ import { IconPin } from '../../../base/icons';
5 5
 import { pinParticipant } from '../../../base/participants';
6 6
 import { connect } from '../../../base/redux';
7 7
 import { AbstractButton, type AbstractButtonProps } from '../../../base/toolbox';
8
+import { shouldDisplayTileView } from '../../../video-layout/functions';
8 9
 
9 10
 export type Props = AbstractButtonProps & {
10 11
 
@@ -59,7 +60,7 @@ class PinButton extends AbstractButton<Props, *> {
59 60
  */
60 61
 function _mapStateToProps(state) {
61 62
     return {
62
-        visible: state['features/video-layout'].tileViewEnabled
63
+        visible: shouldDisplayTileView(state)
63 64
     };
64 65
 }
65 66
 

+ 2
- 1
react/features/toolbox/components/web/Toolbox.js 查看文件

@@ -60,6 +60,7 @@ import {
60 60
 } from '../../../subtitles';
61 61
 import {
62 62
     TileViewButton,
63
+    shouldDisplayTileView,
63 64
     toggleTileView
64 65
 } from '../../../video-layout';
65 66
 import {
@@ -1416,7 +1417,7 @@ function _mapStateToProps(state) {
1416 1417
         _feedbackConfigured: Boolean(callStatsID),
1417 1418
         _isGuest: state['features/base/jwt'].isGuest,
1418 1419
         _fullScreen: fullScreen,
1419
-        _tileViewEnabled: state['features/video-layout'].tileViewEnabled,
1420
+        _tileViewEnabled: shouldDisplayTileView(state),
1420 1421
         _localParticipantID: localParticipant.id,
1421 1422
         _localRecState: localRecordingStates,
1422 1423
         _locked: locked,

+ 5
- 4
react/features/video-layout/actions.js 查看文件

@@ -6,6 +6,7 @@ import {
6 6
     SCREEN_SHARE_PARTICIPANTS_UPDATED,
7 7
     SET_TILE_VIEW
8 8
 } from './actionTypes';
9
+import { shouldDisplayTileView } from './functions';
9 10
 
10 11
 /**
11 12
  * Creates a (redux) action which signals that the list of known participants
@@ -32,10 +33,10 @@ export function setParticipantsWithScreenShare(participantIds: Array<string>) {
32 33
  * @param {boolean} enabled - Whether or not tile view should be shown.
33 34
  * @returns {{
34 35
  *     type: SET_TILE_VIEW,
35
- *     enabled: boolean
36
+ *     enabled: ?boolean
36 37
  * }}
37 38
  */
38
-export function setTileView(enabled: boolean) {
39
+export function setTileView(enabled: ?boolean) {
39 40
     return {
40 41
         type: SET_TILE_VIEW,
41 42
         enabled
@@ -50,8 +51,8 @@ export function setTileView(enabled: boolean) {
50 51
  */
51 52
 export function toggleTileView() {
52 53
     return (dispatch: Dispatch<any>, getState: Function) => {
53
-        const { tileViewEnabled } = getState()['features/video-layout'];
54
+        const tileViewActive = shouldDisplayTileView(getState());
54 55
 
55
-        dispatch(setTileView(!tileViewEnabled));
56
+        dispatch(setTileView(!tileViewActive));
56 57
     };
57 58
 }

+ 5
- 2
react/features/video-layout/components/TileViewButton.js 查看文件

@@ -9,12 +9,14 @@ import {
9 9
 import { TILE_VIEW_ENABLED, getFeatureFlag } from '../../base/flags';
10 10
 import { translate } from '../../base/i18n';
11 11
 import { IconTileView } from '../../base/icons';
12
+import { getParticipantCount } from '../../base/participants';
12 13
 import { connect } from '../../base/redux';
13 14
 import {
14 15
     AbstractButton,
15 16
     type AbstractButtonProps
16 17
 } from '../../base/toolbox';
17 18
 import { setTileView } from '../actions';
19
+import { shouldDisplayTileView } from '../functions';
18 20
 import logger from '../logger';
19 21
 
20 22
 /**
@@ -88,10 +90,11 @@ class TileViewButton<P: Props> extends AbstractButton<P, *> {
88 90
  */
89 91
 function _mapStateToProps(state, ownProps) {
90 92
     const enabled = getFeatureFlag(state, TILE_VIEW_ENABLED, true);
91
-    const { visible = enabled } = ownProps;
93
+    const lonelyMeeting = getParticipantCount(state) < 2;
94
+    const { visible = enabled && !lonelyMeeting } = ownProps;
92 95
 
93 96
     return {
94
-        _tileViewEnabled: state['features/video-layout'].tileViewEnabled,
97
+        _tileViewEnabled: shouldDisplayTileView(state),
95 98
         visible
96 99
     };
97 100
 }

+ 41
- 13
react/features/video-layout/functions.js 查看文件

@@ -1,6 +1,7 @@
1 1
 // @flow
2 2
 
3
-import { getPinnedParticipant } from '../base/participants';
3
+import { getPinnedParticipant, getParticipantCount } from '../base/participants';
4
+import { isYoutubeVideoPlaying } from '../youtube-player';
4 5
 
5 6
 import { LAYOUTS } from './constants';
6 7
 
@@ -72,17 +73,44 @@ export function getTileViewGridDimensions(state: Object, maxColumns: number = ge
72 73
  * @returns {boolean} True if tile view should be displayed.
73 74
  */
74 75
 export function shouldDisplayTileView(state: Object = {}) {
75
-    return Boolean(
76
-        state['features/video-layout']
77
-            && state['features/video-layout'].tileViewEnabled
78
-            && (!state['features/etherpad']
79
-                || !state['features/etherpad'].editing)
80
-
81
-            // Truthy check is needed for interfaceConfig to prevent errors on
82
-            // mobile which does not have interfaceConfig. On web, tile view
83
-            // should never be enabled for filmstrip only mode.
84
-            && (typeof interfaceConfig === 'undefined'
85
-                || !interfaceConfig.filmStripOnly)
86
-            && !getPinnedParticipant(state)
76
+    const participantCount = getParticipantCount(state);
77
+
78
+    // In case of a lonely meeting, we don't allow tile view.
79
+    // But it's a special case too, as we don't even render the button,
80
+    // see TileViewButton component.
81
+    if (participantCount < 2) {
82
+        return false;
83
+    }
84
+
85
+    const { tileViewEnabled } = state['features/video-layout'];
86
+
87
+    if (tileViewEnabled !== undefined) {
88
+        // If the user explicitly requested a view mode, we
89
+        // do that.
90
+        return tileViewEnabled;
91
+    }
92
+
93
+    // None tile view mode is easier to calculate (no need for many negations), so we do
94
+    // that and negate it only once.
95
+    const shouldDisplayNormalMode = Boolean(
96
+
97
+        // Reasons for normal mode:
98
+
99
+        // Editing etherpad
100
+        state['features/etherpad']?.editing
101
+
102
+        // We're in filmstrip-only mode
103
+        || (typeof interfaceConfig === 'object' && interfaceConfig?.filmStripOnly)
104
+
105
+        // We pinned a participant
106
+        || getPinnedParticipant(state)
107
+
108
+        // It's a 1-on-1 meeting
109
+        || participantCount < 3
110
+
111
+        // There is a shared YouTube video in the meeting
112
+        || isYoutubeVideoPlaying(state)
87 113
     );
114
+
115
+    return !shouldDisplayNormalMode;
88 116
 }

+ 70
- 28
react/features/video-layout/middleware.any.js 查看文件

@@ -1,16 +1,17 @@
1
-import {
2
-    PIN_PARTICIPANT,
3
-    getPinnedParticipant,
4
-    pinParticipant
5
-} from '../base/participants';
6
-import { MiddlewareRegistry } from '../base/redux';
7
-import { SET_DOCUMENT_EDITING_STATUS, toggleDocument } from '../etherpad';
1
+// @flow
2
+
3
+import { getCurrentConference } from '../base/conference';
4
+import { PIN_PARTICIPANT, pinParticipant, getPinnedParticipant } from '../base/participants';
5
+import { MiddlewareRegistry, StateListenerRegistry } from '../base/redux';
6
+import { SET_DOCUMENT_EDITING_STATUS } from '../etherpad';
8 7
 
9 8
 import { SET_TILE_VIEW } from './actionTypes';
10 9
 import { setTileView } from './actions';
11 10
 
12 11
 import './subscriber';
13 12
 
13
+let previousTileViewEnabled;
14
+
14 15
 /**
15 16
  * Middleware which intercepts actions and updates tile view related state.
16 17
  *
@@ -18,41 +19,82 @@ import './subscriber';
18 19
  * @returns {Function}
19 20
  */
20 21
 MiddlewareRegistry.register(store => next => action => {
22
+    const result = next(action);
23
+
21 24
     switch (action.type) {
25
+
26
+    // Actions that temporarily clear the user preferred state of tile view,
27
+    // then re-set it when needed.
22 28
     case PIN_PARTICIPANT: {
23
-        const isPinning = Boolean(action.participant.id);
24
-        const { tileViewEnabled } = store.getState()['features/video-layout'];
29
+        const pinnedParticipant = getPinnedParticipant(store.getState());
25 30
 
26
-        if (isPinning && tileViewEnabled) {
27
-            store.dispatch(setTileView(false));
31
+        if (pinnedParticipant) {
32
+            _storeTileViewStateAndClear(store);
33
+        } else {
34
+            _restoreTileViewState(store);
28 35
         }
29
-
30 36
         break;
31 37
     }
32
-
33 38
     case SET_DOCUMENT_EDITING_STATUS:
34 39
         if (action.editing) {
35
-            store.dispatch(setTileView(false));
40
+            _storeTileViewStateAndClear(store);
41
+        } else {
42
+            _restoreTileViewState(store);
36 43
         }
37
-
38 44
         break;
39 45
 
40
-    case SET_TILE_VIEW: {
41
-        const state = store.getState();
46
+    // Things to update when tile view state changes
47
+    case SET_TILE_VIEW:
48
+        if (action.enabled && getPinnedParticipant(store)) {
49
+            store.dispatch(pinParticipant(null));
50
+        }
51
+    }
52
+
42 53
 
43
-        if (action.enabled) {
44
-            if (getPinnedParticipant(state)) {
45
-                store.dispatch(pinParticipant(null));
46
-            }
54
+    return result;
55
+});
47 56
 
48
-            if (state['features/etherpad'].editing) {
49
-                store.dispatch(toggleDocument());
50
-            }
57
+/**
58
+ * Set up state change listener to perform maintenance tasks when the conference
59
+ * is left or failed.
60
+ */
61
+StateListenerRegistry.register(
62
+    state => getCurrentConference(state),
63
+    (conference, { dispatch }, previousConference) => {
64
+        if (conference !== previousConference) {
65
+            // conference changed, left or failed...
66
+            // Clear tile view state.
67
+            dispatch(setTileView());
51 68
         }
69
+    });
52 70
 
53
-        break;
54
-    }
71
+/**
72
+ * Respores tile view state, if it wasn't updated since then.
73
+ *
74
+ * @param {Object} store - The Redux Store.
75
+ * @returns {void}
76
+ */
77
+function _restoreTileViewState({ dispatch, getState }) {
78
+    const { tileViewEnabled } = getState()['features/video-layout'];
79
+
80
+    if (tileViewEnabled === undefined && previousTileViewEnabled !== undefined) {
81
+        dispatch(setTileView(previousTileViewEnabled));
55 82
     }
56 83
 
57
-    return next(action);
58
-});
84
+    previousTileViewEnabled = undefined;
85
+}
86
+
87
+/**
88
+ * Stores the current tile view state and clears it.
89
+ *
90
+ * @param {Object} store - The Redux Store.
91
+ * @returns {void}
92
+ */
93
+function _storeTileViewStateAndClear({ dispatch, getState }) {
94
+    const { tileViewEnabled } = getState()['features/video-layout'];
95
+
96
+    if (tileViewEnabled !== undefined) {
97
+        previousTileViewEnabled = tileViewEnabled;
98
+        dispatch(setTileView(undefined));
99
+    }
100
+}

+ 1
- 1
react/features/video-layout/middleware.web.js 查看文件

@@ -69,7 +69,7 @@ MiddlewareRegistry.register(store => next => action => {
69 69
         break;
70 70
 
71 71
     case PIN_PARTICIPANT:
72
-        VideoLayout.onPinChange(action.participant.id);
72
+        VideoLayout.onPinChange(action.participant?.id);
73 73
         break;
74 74
 
75 75
     case SET_FILMSTRIP_VISIBLE:

+ 5
- 6
react/features/video-layout/reducer.js 查看文件

@@ -1,6 +1,6 @@
1 1
 // @flow
2 2
 
3
-import { PersistenceRegistry, ReducerRegistry } from '../base/redux';
3
+import { ReducerRegistry } from '../base/redux';
4 4
 
5 5
 import {
6 6
     SCREEN_SHARE_PARTICIPANTS_UPDATED,
@@ -14,18 +14,17 @@ const DEFAULT_STATE = {
14 14
      * The indicator which determines whether the video layout should display
15 15
      * video thumbnails in a tiled layout.
16 16
      *
17
+     * Note: undefined means that the user hasn't requested anything in particular yet, so
18
+     * we use our auto switching rules.
19
+     *
17 20
      * @public
18 21
      * @type {boolean}
19 22
      */
20
-    tileViewEnabled: false
23
+    tileViewEnabled: undefined
21 24
 };
22 25
 
23 26
 const STORE_NAME = 'features/video-layout';
24 27
 
25
-PersistenceRegistry.register(STORE_NAME, {
26
-    tileViewEnabled: true
27
-});
28
-
29 28
 ReducerRegistry.register(STORE_NAME, (state = DEFAULT_STATE, action) => {
30 29
     switch (action.type) {
31 30
     case SCREEN_SHARE_PARTICIPANTS_UPDATED: {

+ 6
- 14
react/features/video-layout/subscriber.js 查看文件

@@ -2,16 +2,12 @@
2 2
 
3 3
 import debounce from 'lodash/debounce';
4 4
 
5
-import {
6
-    getPinnedParticipant,
7
-    pinParticipant
8
-} from '../base/participants';
5
+import { pinParticipant, getPinnedParticipant } from '../base/participants';
9 6
 import { StateListenerRegistry, equals } from '../base/redux';
10 7
 import { isFollowMeActive } from '../follow-me';
11 8
 import { selectParticipant } from '../large-video';
12 9
 
13 10
 import { setParticipantsWithScreenShare } from './actions';
14
-import { shouldDisplayTileView } from './functions';
15 11
 
16 12
 declare var APP: Object;
17 13
 declare var interfaceConfig: Object;
@@ -21,17 +17,11 @@ declare var interfaceConfig: Object;
21 17
  * preferred layout state and dispatching additional actions.
22 18
  */
23 19
 StateListenerRegistry.register(
24
-    /* selector */ state => shouldDisplayTileView(state),
25
-    /* listener */ (displayTileView, store) => {
20
+    /* selector */ state => state['features/video-layout'].tileViewEnabled,
21
+    /* listener */ (tileViewEnabled, store) => {
26 22
         const { dispatch } = store;
27 23
 
28 24
         dispatch(selectParticipant());
29
-
30
-        if (!displayTileView) {
31
-            if (_getAutoPinSetting()) {
32
-                _updateAutoPinnedParticipant(store);
33
-            }
34
-        }
35 25
     }
36 26
 );
37 27
 
@@ -115,9 +105,11 @@ function _updateAutoPinnedParticipant({ dispatch, getState }) {
115 105
     const latestScreenshareParticipantId
116 106
         = screenShares[screenShares.length - 1];
117 107
 
108
+    const pinned = getPinnedParticipant(getState);
109
+
118 110
     if (latestScreenshareParticipantId) {
119 111
         dispatch(pinParticipant(latestScreenshareParticipantId));
120
-    } else if (getPinnedParticipant(state['features/base/participants'])) {
112
+    } else if (pinned) {
121 113
         dispatch(pinParticipant(null));
122 114
     }
123 115
 }

+ 6
- 0
react/features/youtube-player/components/_.web.js 查看文件

@@ -0,0 +1,6 @@
1
+// @flow
2
+
3
+import { Component } from 'react';
4
+
5
+export { Component as EnterVideoLinkPrompt };
6
+export { Component as YoutubeLargeVideo };

+ 2
- 0
react/features/youtube-player/components/index.js 查看文件

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

+ 2
- 0
react/features/youtube-player/components/native/index.js 查看文件

@@ -1,2 +1,4 @@
1
+// @flow
2
+
1 3
 export { default as EnterVideoLinkPrompt } from './EnterVideoLinkPrompt';
2 4
 export { default as YoutubeLargeVideo } from './YoutubeLargeVideo';

+ 6
- 0
react/features/youtube-player/constants.js 查看文件

@@ -0,0 +1,6 @@
1
+// @flow
2
+
3
+/**
4
+ * Fixed name of the YouTube player fake participant.
5
+ */
6
+export const YOUTUBE_PARTICIPANT_NAME = 'YouTube';

+ 15
- 0
react/features/youtube-player/functions.js 查看文件

@@ -0,0 +1,15 @@
1
+// @flow
2
+
3
+import { getParticipants } from '../base/participants';
4
+
5
+import { YOUTUBE_PARTICIPANT_NAME } from './constants';
6
+
7
+/**
8
+ * Returns true if there is a youtube video being shaerd in the meeting.
9
+ *
10
+ * @param {Object | Function} stateful - The Redux state or a function that gets resolved to the Redux state.
11
+ * @returns {boolean}
12
+ */
13
+export function isYoutubeVideoPlaying(stateful: Object | Function): boolean {
14
+    return Boolean(getParticipants(stateful).find(p => p.isFakeParticipant && p.name === YOUTUBE_PARTICIPANT_NAME));
15
+}

+ 4
- 0
react/features/youtube-player/index.js 查看文件

@@ -1,3 +1,7 @@
1
+// @flow
2
+
1 3
 export * from './actions';
2 4
 export * from './actionTypes';
3 5
 export * from './components';
6
+export * from './constants';
7
+export * from './functions';

+ 2
- 1
react/features/youtube-player/middleware.js 查看文件

@@ -12,6 +12,7 @@ import { MiddlewareRegistry, StateListenerRegistry } from '../base/redux';
12 12
 
13 13
 import { TOGGLE_SHARED_VIDEO, SET_SHARED_VIDEO_STATUS } from './actionTypes';
14 14
 import { setSharedVideoStatus, showEnterVideoLinkPrompt } from './actions';
15
+import { YOUTUBE_PARTICIPANT_NAME } from './constants';
15 16
 
16 17
 const SHARED_VIDEO = 'shared-video';
17 18
 
@@ -105,7 +106,7 @@ function handleSharingVideoStatus(store, videoId, { state, time, from }, confere
105 106
             id: videoId,
106 107
             isFakeParticipant: true,
107 108
             avatarURL: `https://img.youtube.com/vi/${videoId}/0.jpg`,
108
-            name: 'YouTube'
109
+            name: YOUTUBE_PARTICIPANT_NAME
109 110
         }));
110 111
 
111 112
         dispatch(pinParticipant(videoId));

Loading…
取消
儲存