浏览代码

feat(shared-video) refactor dialog to use React

Also unify the mobile and web features into one, even though internally they still have separate ways to enable the functionality.
j8
Calinteodor 4 年前
父节点
当前提交
430591bd1e
没有帐户链接到提交者的电子邮件
共有 42 个文件被更改,包括 594 次插入459 次删除
  1. 1
    1
      conference.js
  2. 2
    1
      lang/main.json
  3. 19
    0
      modules/UI/UI.js
  4. 26
    179
      modules/UI/shared_video/SharedVideo.js
  5. 1
    1
      react/features/app/middlewares.native.js
  6. 1
    1
      react/features/app/reducers.native.js
  7. 1
    1
      react/features/base/flags/functions.js
  8. 1
    1
      react/features/base/participants/components/ParticipantView.native.js
  9. 15
    2
      react/features/shared-video/actionTypes.js
  10. 0
    31
      react/features/shared-video/actions.js
  11. 12
    12
      react/features/shared-video/actions.native.js
  12. 62
    0
      react/features/shared-video/actions.web.js
  13. 16
    22
      react/features/shared-video/components/AbstractSharedVideoDialog.js
  14. 0
    0
      react/features/shared-video/components/_.native.js
  15. 1
    0
      react/features/shared-video/components/_.web.js
  16. 1
    0
      react/features/shared-video/components/index.js
  17. 14
    22
      react/features/shared-video/components/native/SharedVideoButton.js
  18. 5
    4
      react/features/shared-video/components/native/SharedVideoDialog.js
  19. 3
    3
      react/features/shared-video/components/native/YoutubeLargeVideo.js
  20. 6
    0
      react/features/shared-video/components/native/index.js
  21. 0
    0
      react/features/shared-video/components/native/styles.js
  22. 112
    0
      react/features/shared-video/components/web/SharedVideoButton.js
  23. 102
    0
      react/features/shared-video/components/web/SharedVideoDialog.js
  24. 5
    0
      react/features/shared-video/components/web/index.js
  25. 19
    0
      react/features/shared-video/constants.js
  26. 44
    0
      react/features/shared-video/functions.js
  27. 0
    2
      react/features/shared-video/index.js
  28. 0
    30
      react/features/shared-video/middleware.js
  29. 15
    15
      react/features/shared-video/middleware.native.js
  30. 63
    0
      react/features/shared-video/middleware.web.js
  31. 8
    2
      react/features/shared-video/reducer.native.js
  32. 29
    0
      react/features/shared-video/reducer.web.js
  33. 2
    2
      react/features/toolbox/components/native/OverflowMenu.js
  34. 6
    43
      react/features/toolbox/components/web/Toolbox.js
  35. 2
    2
      react/features/video-layout/functions.js
  36. 0
    22
      react/features/youtube-player/actionTypes.js
  37. 0
    6
      react/features/youtube-player/components/_.web.js
  38. 0
    5
      react/features/youtube-player/components/index.js
  39. 0
    4
      react/features/youtube-player/components/native/index.js
  40. 0
    6
      react/features/youtube-player/constants.js
  41. 0
    15
      react/features/youtube-player/functions.js
  42. 0
    24
      react/features/youtube-player/reducer.js

+ 1
- 1
conference.js 查看文件

@@ -125,7 +125,7 @@ import {
125 125
 } from './react/features/prejoin';
126 126
 import { disableReceiver, stopReceiver } from './react/features/remote-control';
127 127
 import { toggleScreenshotCaptureEffect } from './react/features/screenshot-capture';
128
-import { setSharedVideoStatus } from './react/features/shared-video';
128
+import { setSharedVideoStatus } from './react/features/shared-video/actions';
129 129
 import { AudioMixerEffect } from './react/features/stream-effects/audio-mixer/AudioMixerEffect';
130 130
 import { createPresenterEffect } from './react/features/stream-effects/presenter';
131 131
 import { endpointMessageReceived } from './react/features/subtitles';

+ 2
- 1
lang/main.json 查看文件

@@ -313,6 +313,7 @@
313 313
         "unlockRoom": "Remove meeting $t(lockRoomPassword)",
314 314
         "user": "user",
315 315
         "userPassword": "user password",
316
+        "videoLink": "Video link",
316 317
         "WaitForHostMsg": "The conference <b>{{room}}</b> has not yet started. If you are the host then please authenticate. Otherwise, please wait for the host to arrive.",
317 318
         "WaitForHostMsgWOk": "The conference <b>{{room}}</b> has not yet started. If you are the host then please press Ok to authenticate. Otherwise, please wait for the host to arrive.",
318 319
         "WaitingForHost": "Waiting for the host ...",
@@ -737,7 +738,7 @@
737 738
             "remoteVideoMute": "Disable camera of participant",
738 739
             "security": "Security options",
739 740
             "Settings": "Toggle settings",
740
-            "sharedvideo": "Toggle Youtube video sharing",
741
+            "sharedvideo": "Toggle YouTube video sharing",
741 742
             "shareRoom": "Invite someone",
742 743
             "shareYourScreen": "Toggle screenshare",
743 744
             "shortcuts": "Toggle shortcuts",

+ 19
- 0
modules/UI/UI.js 查看文件

@@ -491,6 +491,25 @@ UI.onSharedVideoStop = function(id, attributes) {
491 491
     }
492 492
 };
493 493
 
494
+/**
495
+ * Show shared video.
496
+ * @param {string} url video url
497
+ */
498
+UI.startSharedVideoEmitter = function(url) {
499
+    if (sharedVideoManager) {
500
+        sharedVideoManager.startSharedVideoEmitter(url);
501
+    }
502
+};
503
+
504
+/**
505
+ * Stop shared video.
506
+ */
507
+UI.stopSharedVideoEmitter = function() {
508
+    if (sharedVideoManager) {
509
+        sharedVideoManager.stopSharedVideoEmitter();
510
+    }
511
+};
512
+
494 513
 // TODO: Export every function separately. For now there is no point of doing
495 514
 // this because we are importing everything.
496 515
 export default UI;

+ 26
- 179
modules/UI/shared_video/SharedVideo.js 查看文件

@@ -12,11 +12,10 @@ import {
12 12
     participantLeft,
13 13
     pinParticipant
14 14
 } from '../../../react/features/base/participants';
15
+import { VIDEO_PLAYER_PARTICIPANT_NAME } from '../../../react/features/shared-video/constants';
15 16
 import { dockToolbox, showToolbox } from '../../../react/features/toolbox/actions.web';
16 17
 import { getToolboxHeight } from '../../../react/features/toolbox/functions.web';
17
-import { YOUTUBE_PARTICIPANT_NAME } from '../../../react/features/youtube-player/constants';
18 18
 import UIEvents from '../../../service/UI/UIEvents';
19
-import UIUtil from '../util/UIUtil';
20 19
 import Filmstrip from '../videolayout/Filmstrip';
21 20
 import LargeContainer from '../videolayout/LargeContainer';
22 21
 import VideoLayout from '../videolayout/VideoLayout';
@@ -29,14 +28,8 @@ export const SHARED_VIDEO_CONTAINER_TYPE = 'sharedvideo';
29 28
  * Example shared video link.
30 29
  * @type {string}
31 30
  */
32
-const defaultSharedVideoLink = 'https://youtu.be/TB7LlM4erx8';
33 31
 const updateInterval = 5000; // milliseconds
34 32
 
35
-/**
36
- * The dialog for user input (video link).
37
- * @type {null}
38
- */
39
-let dialog = null;
40 33
 
41 34
 /**
42 35
  * Manager of shared video.
@@ -76,52 +69,37 @@ export default class SharedVideoManager {
76 69
     }
77 70
 
78 71
     /**
79
-     * Starts shared video by asking user for url, or if its already working
80
-     * asks whether the user wants to stop sharing the video.
72
+     * Start shared video event emitter if a video is not shown.
73
+     *
74
+     * @param url of the video
81 75
      */
82
-    toggleSharedVideo() {
83
-        if (dialog) {
84
-            return;
85
-        }
76
+    startSharedVideoEmitter(url) {
86 77
 
87 78
         if (!this.isSharedVideoShown) {
88
-            requestVideoLink().then(
89
-                    url => {
90
-                        this.emitter.emit(
91
-                            UIEvents.UPDATE_SHARED_VIDEO, url, 'start');
92
-                        sendAnalytics(createEvent('started'));
93
-                    },
94
-                    err => {
95
-                        logger.log('SHARED VIDEO CANCELED', err);
96
-                        sendAnalytics(createEvent('canceled'));
97
-                    }
98
-            );
79
+            if (url) {
80
+                this.emitter.emit(
81
+                    UIEvents.UPDATE_SHARED_VIDEO, url, 'start');
82
+                sendAnalytics(createEvent('started'));
83
+            }
99 84
 
100
-            return;
85
+            logger.log('SHARED VIDEO CANCELED');
86
+            sendAnalytics(createEvent('canceled'));
101 87
         }
88
+    }
89
+
90
+    /**
91
+     * Stop shared video event emitter done by the one who shared the video.
92
+     */
93
+    stopSharedVideoEmitter() {
102 94
 
103 95
         if (APP.conference.isLocalId(this.from)) {
104
-            showStopVideoPropmpt().then(
105
-                () => {
106
-                    // make sure we stop updates for playing before we send stop
107
-                    // if we stop it after receiving self presence, we can end
108
-                    // up sending stop playing, and on the other end it will not
109
-                    // stop
110
-                    if (this.intervalId) {
111
-                        clearInterval(this.intervalId);
112
-                        this.intervalId = null;
113
-                    }
114
-                    this.emitter.emit(
115
-                        UIEvents.UPDATE_SHARED_VIDEO, this.url, 'stop');
116
-                    sendAnalytics(createEvent('stopped'));
117
-                },
118
-                () => {}); // eslint-disable-line no-empty-function
119
-        } else {
120
-            APP.UI.messageHandler.showWarning({
121
-                descriptionKey: 'dialog.alreadySharedVideoMsg',
122
-                titleKey: 'dialog.alreadySharedVideoTitle'
123
-            });
124
-            sendAnalytics(createEvent('already.shared'));
96
+            if (this.intervalId) {
97
+                clearInterval(this.intervalId);
98
+                this.intervalId = null;
99
+            }
100
+            this.emitter.emit(
101
+                UIEvents.UPDATE_SHARED_VIDEO, this.url, 'stop');
102
+            sendAnalytics(createEvent('stopped'));
125 103
         }
126 104
     }
127 105
 
@@ -303,7 +281,7 @@ export default class SharedVideoManager {
303 281
                 conference: APP.conference._room,
304 282
                 id: self.url,
305 283
                 isFakeParticipant: true,
306
-                name: YOUTUBE_PARTICIPANT_NAME
284
+                name: VIDEO_PLAYER_PARTICIPANT_NAME
307 285
             }));
308 286
 
309 287
             APP.store.dispatch(pinParticipant(self.url));
@@ -675,134 +653,3 @@ class SharedVideoContainer extends LargeContainer {
675 653
         return false;
676 654
     }
677 655
 }
678
-
679
-/**
680
- * Checks if given string is youtube url.
681
- * @param {string} url string to check.
682
- * @returns {boolean}
683
- */
684
-function getYoutubeLink(url) {
685
-    const p = /^(?:https?:\/\/)?(?:www\.)?(?:youtu\.be\/|youtube\.com\/(?:embed\/|v\/|watch\?v=|watch\?.+&v=))((\w|-){11})(?:\S+)?$/;// eslint-disable-line max-len
686
-
687
-
688
-    return url.match(p) ? RegExp.$1 : false;
689
-}
690
-
691
-/**
692
- * Ask user if he want to close shared video.
693
- */
694
-function showStopVideoPropmpt() {
695
-    return new Promise((resolve, reject) => {
696
-        const submitFunction = function(e, v) {
697
-            if (v) {
698
-                resolve();
699
-            } else {
700
-                reject();
701
-            }
702
-        };
703
-
704
-        const closeFunction = function() {
705
-            dialog = null;
706
-        };
707
-
708
-        dialog = APP.UI.messageHandler.openTwoButtonDialog({
709
-            titleKey: 'dialog.removeSharedVideoTitle',
710
-            msgKey: 'dialog.removeSharedVideoMsg',
711
-            leftButtonKey: 'dialog.Remove',
712
-            submitFunction,
713
-            closeFunction
714
-        });
715
-    });
716
-}
717
-
718
-/**
719
- * Ask user for shared video url to share with others.
720
- * Dialog validates client input to allow only youtube urls.
721
- */
722
-function requestVideoLink() {
723
-    const i18n = APP.translation;
724
-    const cancelButton = i18n.generateTranslationHTML('dialog.Cancel');
725
-    const shareButton = i18n.generateTranslationHTML('dialog.Share');
726
-    const backButton = i18n.generateTranslationHTML('dialog.Back');
727
-    const linkError
728
-        = i18n.generateTranslationHTML('dialog.shareVideoLinkError');
729
-
730
-    return new Promise((resolve, reject) => {
731
-        dialog = APP.UI.messageHandler.openDialogWithStates({
732
-            state0: {
733
-                titleKey: 'dialog.shareVideoTitle',
734
-                html: `
735
-                    <input name='sharedVideoUrl' type='text'
736
-                           class='input-control'
737
-                           data-i18n='[placeholder]defaultLink'
738
-                           autofocus>`,
739
-                persistent: false,
740
-                buttons: [
741
-                    { title: cancelButton,
742
-                        value: false },
743
-                    { title: shareButton,
744
-                        value: true }
745
-                ],
746
-                focus: ':input:first',
747
-                defaultButton: 1,
748
-                submit(e, v, m, f) { // eslint-disable-line max-params
749
-                    e.preventDefault();
750
-                    if (!v) {
751
-                        reject('cancelled');
752
-                        dialog.close();
753
-
754
-                        return;
755
-                    }
756
-
757
-                    const sharedVideoUrl = f.sharedVideoUrl;
758
-
759
-                    if (!sharedVideoUrl) {
760
-                        return;
761
-                    }
762
-
763
-                    const urlValue
764
-                        = encodeURI(UIUtil.escapeHtml(sharedVideoUrl));
765
-                    const yVideoId = getYoutubeLink(urlValue);
766
-
767
-                    if (!yVideoId) {
768
-                        dialog.goToState('state1');
769
-
770
-                        return false;
771
-                    }
772
-
773
-                    resolve(yVideoId);
774
-                    dialog.close();
775
-                }
776
-            },
777
-
778
-            state1: {
779
-                titleKey: 'dialog.shareVideoTitle',
780
-                html: linkError,
781
-                persistent: false,
782
-                buttons: [
783
-                    { title: cancelButton,
784
-                        value: false },
785
-                    { title: backButton,
786
-                        value: true }
787
-                ],
788
-                focus: ':input:first',
789
-                defaultButton: 1,
790
-                submit(e, v) {
791
-                    e.preventDefault();
792
-                    if (v === 0) {
793
-                        reject();
794
-                        dialog.close();
795
-                    } else {
796
-                        dialog.goToState('state0');
797
-                    }
798
-                }
799
-            }
800
-        }, {
801
-            close() {
802
-                dialog = null;
803
-            }
804
-        }, {
805
-            url: defaultSharedVideoLink
806
-        });
807
-    });
808
-}

+ 1
- 1
react/features/app/middlewares.native.js 查看文件

@@ -13,6 +13,6 @@ import '../mobile/proximity/middleware';
13 13
 import '../mobile/wake-lock/middleware';
14 14
 import '../mobile/watchos/middleware';
15 15
 import '../share-room/middleware';
16
-import '../youtube-player/middleware';
16
+import '../shared-video/middleware';
17 17
 
18 18
 import './middlewares.any';

+ 1
- 1
react/features/app/reducers.native.js 查看文件

@@ -8,6 +8,6 @@ import '../mobile/external-api/reducer';
8 8
 import '../mobile/full-screen/reducer';
9 9
 import '../mobile/incoming-call/reducer';
10 10
 import '../mobile/watchos/reducer';
11
-import '../youtube-player/reducer';
11
+import '../shared-video/reducer';
12 12
 
13 13
 import './reducers.any';

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

@@ -11,7 +11,7 @@ import { toState } from '../redux';
11 11
  * @param {string} flag - The name of the React {@code Component} prop of
12 12
  * the currently mounted {@code App} to get.
13 13
  * @param {*} defaultValue - A default value for the flag, in case it's not defined.
14
- * @returns {*} The value of the specified React {@code Compoennt} prop of the
14
+ * @returns {*} The value of the specified React {@code Component} prop of the
15 15
  * currently mounted {@code App}.
16 16
  */
17 17
 export function getFeatureFlag(stateful: Function | Object, flag: string, defaultValue: any) {

+ 1
- 1
react/features/base/participants/components/ParticipantView.native.js 查看文件

@@ -3,7 +3,7 @@
3 3
 import React, { Component } from 'react';
4 4
 import { Text, View } from 'react-native';
5 5
 
6
-import { YoutubeLargeVideo } from '../../../youtube-player/components';
6
+import { YoutubeLargeVideo } from '../../../shared-video/components';
7 7
 import { Avatar } from '../../avatar';
8 8
 import { translate } from '../../i18n';
9 9
 import { JitsiParticipantConnectionStatus } from '../../lib-jitsi-meet';

+ 15
- 2
react/features/shared-video/actionTypes.js 查看文件

@@ -1,6 +1,8 @@
1
+// @flow
2
+
1 3
 /**
2 4
  * The type of the action which signals to update the current known state of the
3
- * shared YouTube video.
5
+ * shared video.
4 6
  *
5 7
  * {
6 8
  *     type: SET_SHARED_VIDEO_STATUS,
@@ -11,10 +13,21 @@ export const SET_SHARED_VIDEO_STATUS = 'SET_SHARED_VIDEO_STATUS';
11 13
 
12 14
 /**
13 15
  * The type of the action which signals to start the flow for starting or
14
- * stopping a shared YouTube video.
16
+ * stopping a shared video.
15 17
  *
16 18
  * {
17 19
  *     type: TOGGLE_SHARED_VIDEO
18 20
  * }
19 21
  */
20 22
 export const TOGGLE_SHARED_VIDEO = 'TOGGLE_SHARED_VIDEO';
23
+
24
+
25
+/**
26
+ * The type of the action which signals to disable or enable the shared video
27
+ * button.
28
+ *
29
+ * {
30
+ *     type: SET_DISABLE_BUTTON
31
+ * }
32
+ */
33
+export const SET_DISABLE_BUTTON = 'SET_DISABLE_BUTTON';

+ 0
- 31
react/features/shared-video/actions.js 查看文件

@@ -1,31 +0,0 @@
1
-import { SET_SHARED_VIDEO_STATUS, TOGGLE_SHARED_VIDEO } from './actionTypes';
2
-
3
-/**
4
- * Updates the current known status of the shared YouTube video.
5
- *
6
- * @param {string} status - The current status of the YouTube video being
7
- * shared.
8
- * @returns {{
9
- *     type: SET_SHARED_VIDEO_STATUS,
10
- *     status: string
11
- * }}
12
- */
13
-export function setSharedVideoStatus(status) {
14
-    return {
15
-        type: SET_SHARED_VIDEO_STATUS,
16
-        status
17
-    };
18
-}
19
-
20
-/**
21
- * Starts the flow for starting or stopping a shared YouTube video.
22
- *
23
- * @returns {{
24
- *     type: TOGGLE_SHARED_VIDEO
25
- * }}
26
- */
27
-export function toggleSharedVideo() {
28
-    return {
29
-        type: TOGGLE_SHARED_VIDEO
30
-    };
31
-}

react/features/youtube-player/actions.js → react/features/shared-video/actions.native.js 查看文件

@@ -2,16 +2,16 @@
2 2
 
3 3
 import { openDialog } from '../base/dialog';
4 4
 
5
-import { SET_SHARED_VIDEO_STATUS } from './actionTypes';
6
-import { EnterVideoLinkPrompt } from './components';
5
+import { SET_SHARED_VIDEO_STATUS, TOGGLE_SHARED_VIDEO } from './actionTypes';
6
+import { SharedVideoDialog } from './components/native';
7 7
 
8 8
 /**
9
- * Updates the current known status of the shared YouTube video.
9
+ * Updates the current known status of the shared video.
10 10
  *
11
- * @param {string} videoId - The youtubeId of the video to be shared.
12
- * @param {string} status - The current status of the YouTube video being shared.
13
- * @param {number} time - The current position of the YouTube video being shared.
14
- * @param {string} ownerId - The participantId of the user sharing the YouTube video.
11
+ * @param {string} videoId - The id of the video to be shared.
12
+ * @param {string} status - The current status of the video being shared.
13
+ * @param {number} time - The current position of the video being shared.
14
+ * @param {string} ownerId - The participantId of the user sharing the video.
15 15
  * @returns {{
16 16
  *     type: SET_SHARED_VIDEO_STATUS,
17 17
  *     ownerId: string,
@@ -31,7 +31,7 @@ export function setSharedVideoStatus(videoId: string, status: string, time: numb
31 31
 }
32 32
 
33 33
 /**
34
- * Starts the flow for starting or stopping a shared YouTube video.
34
+ * Starts the flow for starting or stopping a shared video.
35 35
  *
36 36
  * @returns {{
37 37
  *     type: TOGGLE_SHARED_VIDEO
@@ -39,16 +39,16 @@ export function setSharedVideoStatus(videoId: string, status: string, time: numb
39 39
  */
40 40
 export function toggleSharedVideo() {
41 41
     return {
42
-        type: 'TOGGLE_SHARED_VIDEO'
42
+        type: TOGGLE_SHARED_VIDEO
43 43
     };
44 44
 }
45 45
 
46 46
 /**
47
- * Displays the prompt for entering the youtube video link.
47
+ * Displays the prompt for entering the video link.
48 48
  *
49 49
  * @param {Function} onPostSubmit - The function to be invoked when a valid link is entered.
50 50
  * @returns {Function}
51 51
  */
52
-export function showEnterVideoLinkPrompt(onPostSubmit: ?Function) {
53
-    return openDialog(EnterVideoLinkPrompt, { onPostSubmit });
52
+export function showSharedVideoDialog(onPostSubmit: ?Function) {
53
+    return openDialog(SharedVideoDialog, { onPostSubmit });
54 54
 }

+ 62
- 0
react/features/shared-video/actions.web.js 查看文件

@@ -0,0 +1,62 @@
1
+// @flow
2
+
3
+import { openDialog } from '../base/dialog/actions';
4
+import { SharedVideoDialog } from '../shared-video/components';
5
+
6
+import { SET_SHARED_VIDEO_STATUS, TOGGLE_SHARED_VIDEO, SET_DISABLE_BUTTON } from './actionTypes';
7
+
8
+/**
9
+ * Updates the current known status of the shared video.
10
+ *
11
+ * @param {string} status - The current status of the video being shared.
12
+ * @returns {{
13
+ *     type: SET_SHARED_VIDEO_STATUS,
14
+ *     status: string
15
+ * }}
16
+ */
17
+export function setSharedVideoStatus(status: string) {
18
+    return {
19
+        type: SET_SHARED_VIDEO_STATUS,
20
+        status
21
+    };
22
+}
23
+
24
+
25
+/**
26
+ * Disabled share video button.
27
+ *
28
+ * @param {boolean} disabled - The current state of the share video button.
29
+ * @returns {{
30
+ *     type: SET_DISABLE_BUTTON,
31
+ *     disabled: boolean
32
+ * }}
33
+ */
34
+export function setDisableButton(disabled: boolean) {
35
+    return {
36
+        type: SET_DISABLE_BUTTON,
37
+        disabled
38
+    };
39
+}
40
+
41
+/**
42
+ * Starts the flow for starting or stopping a shared video.
43
+ *
44
+ * @returns {{
45
+ *     type: TOGGLE_SHARED_VIDEO
46
+ * }}
47
+ */
48
+export function toggleSharedVideo() {
49
+    return {
50
+        type: TOGGLE_SHARED_VIDEO
51
+    };
52
+}
53
+
54
+/**
55
+ * Displays the dialog for entering the video link.
56
+ *
57
+ * @param {Function} onPostSubmit - The function to be invoked when a valid link is entered.
58
+ * @returns {Function}
59
+ */
60
+export function showSharedVideoDialog(onPostSubmit: ?Function) {
61
+    return openDialog(SharedVideoDialog, { onPostSubmit });
62
+}

react/features/youtube-player/components/AbstractEnterVideoLinkPrompt.js → react/features/shared-video/components/AbstractSharedVideoDialog.js 查看文件

@@ -3,31 +3,38 @@
3 3
 import { Component } from 'react';
4 4
 import type { Dispatch } from 'redux';
5 5
 
6
+import { getYoutubeLink } from '../functions';
7
+
8
+
6 9
 /**
7 10
  * The type of the React {@code Component} props of
8
- * {@link AbstractEnterVideoLinkPrompt}.
11
+ * {@link AbstractSharedVideoDialog}.
9 12
  */
10 13
 export type Props = {
11 14
 
12 15
     /**
13
-     * Invoked to update the shared youtube video link.
16
+     * Invoked to update the shared video link.
14 17
      */
15 18
     dispatch: Dispatch<any>,
16 19
 
17 20
     /**
18
-     * Function to be invoked after typing a valid youtube video .
21
+     * Function to be invoked after typing a valid video.
22
+     */
23
+    onPostSubmit: ?Function,
24
+
25
+    /**
26
+     * Invoked to obtain translated strings.
19 27
      */
20
-    onPostSubmit: ?Function
28
+    t: Function
21 29
 };
22 30
 
23 31
 /**
24
- * Implements an abstract class for {@code EnterVideoLinkPrompt}.
32
+ * Implements an abstract class for {@code SharedVideoDialog}.
25 33
  */
26
-export default class AbstractEnterVideoLinkPrompt<S: *> extends Component < Props, S > {
34
+export default class AbstractSharedVideoDialog<S: *> extends Component < Props, S > {
27 35
     /**
28 36
      * Instantiates a new component.
29 37
      *
30
-     *
31 38
      * @inheritdoc
32 39
      */
33 40
     constructor(props: Props) {
@@ -39,7 +46,7 @@ export default class AbstractEnterVideoLinkPrompt<S: *> extends Component < Prop
39 46
     _onSetVideoLink: string => boolean;
40 47
 
41 48
     /**
42
-     * Validates the entered video link by extractibg the id and dispatches it.
49
+     * Validates the entered video link by extracting the id and dispatches it.
43 50
      *
44 51
      * It returns a boolean to comply the Dialog behaviour:
45 52
      *     {@code true} - the dialog should be closed.
@@ -48,7 +55,7 @@ export default class AbstractEnterVideoLinkPrompt<S: *> extends Component < Prop
48 55
      * @param {string} link - The entered video link.
49 56
      * @returns {boolean}
50 57
      */
51
-    _onSetVideoLink(link) {
58
+    _onSetVideoLink(link: string) {
52 59
         if (!link || !link.trim()) {
53 60
             return false;
54 61
         }
@@ -67,17 +74,4 @@ export default class AbstractEnterVideoLinkPrompt<S: *> extends Component < Prop
67 74
     }
68 75
 }
69 76
 
70
-/**
71
- * Validates the entered video url.
72
- *
73
- * It returns a boolean to reflect whether the url matches the youtube regex.
74
- *
75
- * @param {string} url - The entered video link.
76
- * @returns {boolean}
77
- */
78
-function getYoutubeLink(url) {
79
-    const p = /^(?:https?:\/\/)?(?:www\.)?(?:youtu\.be\/|(?:m\.)?youtube\.com\/(?:embed\/|v\/|watch\?v=|watch\?.+&v=))((\w|-){11})(?:\S+)?$/;// eslint-disable-line max-len
80
-    const result = url.match(p);
81 77
 
82
-    return result ? result[1] : false;
83
-}

react/features/youtube-player/components/_.native.js → react/features/shared-video/components/_.native.js 查看文件


+ 1
- 0
react/features/shared-video/components/_.web.js 查看文件

@@ -0,0 +1 @@
1
+export * from './web';

+ 1
- 0
react/features/shared-video/components/index.js 查看文件

@@ -0,0 +1 @@
1
+export * from './_';

react/features/youtube-player/components/VideoShareButton.js → react/features/shared-video/components/native/SharedVideoButton.js 查看文件

@@ -2,13 +2,14 @@
2 2
 
3 3
 import type { Dispatch } from 'redux';
4 4
 
5
-import { getFeatureFlag, VIDEO_SHARE_BUTTON_ENABLED } from '../../base/flags';
6
-import { translate } from '../../base/i18n';
7
-import { IconShareVideo } from '../../base/icons';
8
-import { getLocalParticipant } from '../../base/participants';
9
-import { connect } from '../../base/redux';
10
-import { AbstractButton, type AbstractButtonProps } from '../../base/toolbox/components';
11
-import { toggleSharedVideo } from '../actions';
5
+import { getFeatureFlag, VIDEO_SHARE_BUTTON_ENABLED } from '../../../base/flags';
6
+import { translate } from '../../../base/i18n';
7
+import { IconShareVideo } from '../../../base/icons';
8
+import { getLocalParticipant } from '../../../base/participants';
9
+import { connect } from '../../../base/redux';
10
+import { AbstractButton, type AbstractButtonProps } from '../../../base/toolbox/components';
11
+import { toggleSharedVideo } from '../../actions.native';
12
+import { isSharingStatus } from '../../functions';
12 13
 
13 14
 /**
14 15
  * The type of the React {@code Component} props of {@link TileViewButton}.
@@ -21,7 +22,7 @@ type Props = AbstractButtonProps & {
21 22
     _isDisabled: boolean,
22 23
 
23 24
     /**
24
-     * Whether or not the local participant is sharing a YouTube video.
25
+     * Whether or not the local participant is sharing a video.
25 26
      */
26 27
     _sharingVideo: boolean,
27 28
 
@@ -76,7 +77,7 @@ class VideoShareButton extends AbstractButton<Props, *> {
76 77
     }
77 78
 
78 79
     /**
79
-     * Dispatches an action to toggle YouTube video sharing.
80
+     * Dispatches an action to toggle video sharing.
80 81
      *
81 82
      * @private
82 83
      * @returns {void}
@@ -95,7 +96,7 @@ class VideoShareButton extends AbstractButton<Props, *> {
95 96
  * @returns {Props}
96 97
  */
97 98
 function _mapStateToProps(state, ownProps): Object {
98
-    const { ownerId, status: sharedVideoStatus } = state['features/youtube-player'];
99
+    const { ownerId, status: sharedVideoStatus } = state['features/shared-video'];
99 100
     const localParticipantId = getLocalParticipant(state).id;
100 101
     const enabled = getFeatureFlag(state, VIDEO_SHARE_BUTTON_ENABLED, true);
101 102
     const { visible = enabled } = ownProps;
@@ -104,24 +105,15 @@ function _mapStateToProps(state, ownProps): Object {
104 105
         return {
105 106
             _isDisabled: isSharingStatus(sharedVideoStatus),
106 107
             _sharingVideo: false,
107
-            visible };
108
+            visible
109
+        };
108 110
     }
109 111
 
110 112
     return {
113
+        _isDisabled: false,
111 114
         _sharingVideo: isSharingStatus(sharedVideoStatus),
112 115
         visible
113 116
     };
114 117
 }
115 118
 
116
-/**
117
- * Checks if the status is one that is actually sharing the video - playing, pause or start.
118
- *
119
- * @param {string} status - The shared video status.
120
- * @private
121
- * @returns {boolean}
122
- */
123
-function isSharingStatus(status) {
124
-    return [ 'playing', 'pause', 'start' ].includes(status);
125
-}
126
-
127 119
 export default translate(connect(_mapStateToProps)(VideoShareButton));

react/features/youtube-player/components/native/EnterVideoLinkPrompt.js → react/features/shared-video/components/native/SharedVideoDialog.js 查看文件

@@ -4,12 +4,13 @@ import React from 'react';
4 4
 
5 5
 import { InputDialog } from '../../../base/dialog';
6 6
 import { connect } from '../../../base/redux';
7
-import AbstractEnterVideoLinkPrompt from '../AbstractEnterVideoLinkPrompt';
7
+import { defaultSharedVideoLink } from '../../constants';
8
+import AbstractSharedVideoDialog from '../AbstractSharedVideoDialog';
8 9
 
9 10
 /**
10 11
  * Implements a component to render a display name prompt.
11 12
  */
12
-class EnterVideoLinkPrompt extends AbstractEnterVideoLinkPrompt<*> {
13
+class SharedVideoDialog extends AbstractSharedVideoDialog<*> {
13 14
     /**
14 15
      * Implements React's {@link Component#render()}.
15 16
      *
@@ -21,7 +22,7 @@ class EnterVideoLinkPrompt extends AbstractEnterVideoLinkPrompt<*> {
21 22
                 contentKey = 'dialog.shareVideoTitle'
22 23
                 onSubmit = { this._onSetVideoLink }
23 24
                 textInputProps = {{
24
-                    placeholder: 'https://youtu.be/TB7LlM4erx8'
25
+                    placeholder: defaultSharedVideoLink
25 26
                 }} />
26 27
         );
27 28
     }
@@ -29,4 +30,4 @@ class EnterVideoLinkPrompt extends AbstractEnterVideoLinkPrompt<*> {
29 30
     _onSetVideoLink: string => boolean;
30 31
 }
31 32
 
32
-export default connect()(EnterVideoLinkPrompt);
33
+export default connect()(SharedVideoDialog);

react/features/youtube-player/components/native/YoutubeLargeVideo.js → react/features/shared-video/components/native/YoutubeLargeVideo.js 查看文件

@@ -6,9 +6,9 @@ import YoutubePlayer from 'react-native-youtube-iframe';
6 6
 
7 7
 import { getLocalParticipant } from '../../../base/participants';
8 8
 import { connect } from '../../../base/redux';
9
-import { ASPECT_RATIO_WIDE } from '../../../base/responsive-ui/constants';
9
+import { ASPECT_RATIO_WIDE } from '../../../base/responsive-ui';
10 10
 import { setToolboxVisible } from '../../../toolbox/actions';
11
-import { setSharedVideoStatus } from '../../actions';
11
+import { setSharedVideoStatus } from '../../actions.native';
12 12
 
13 13
 import styles from './styles';
14 14
 
@@ -383,7 +383,7 @@ function shouldSeekToPosition(newTime, previousTime) {
383 383
  * @returns {Props}
384 384
  */
385 385
 function _mapStateToProps(state) {
386
-    const { ownerId, status, time } = state['features/youtube-player'];
386
+    const { ownerId, status, time } = state['features/shared-video'];
387 387
     const localParticipant = getLocalParticipant(state);
388 388
     const responsiveUi = state['features/base/responsive-ui'];
389 389
     const { aspectRatio, clientHeight: screenHeight, clientWidth: screenWidth } = responsiveUi;

+ 6
- 0
react/features/shared-video/components/native/index.js 查看文件

@@ -0,0 +1,6 @@
1
+// @flow
2
+
3
+export { default as SharedVideoButton } from './SharedVideoButton';
4
+export { default as SharedVideoDialog } from './SharedVideoDialog';
5
+export { default as YoutubeLargeVideo } from './YoutubeLargeVideo';
6
+

react/features/youtube-player/components/native/styles.js → react/features/shared-video/components/native/styles.js 查看文件


+ 112
- 0
react/features/shared-video/components/web/SharedVideoButton.js 查看文件

@@ -0,0 +1,112 @@
1
+// @flow
2
+
3
+import type { Dispatch } from 'redux';
4
+
5
+import { translate } from '../../../base/i18n';
6
+import { IconShareVideo } from '../../../base/icons';
7
+import { connect } from '../../../base/redux';
8
+import {
9
+    AbstractButton,
10
+    type AbstractButtonProps
11
+} from '../../../base/toolbox/components';
12
+import { showSharedVideoDialog } from '../../actions.web';
13
+import { isSharingStatus } from '../../functions';
14
+
15
+declare var APP: Object;
16
+
17
+type Props = AbstractButtonProps & {
18
+
19
+    /**
20
+     * The redux {@code dispatch} function.
21
+     */
22
+    dispatch: Dispatch<any>,
23
+
24
+    /**
25
+     * Whether or not the button is disabled.
26
+     */
27
+    _isDisabled: boolean,
28
+
29
+    /**
30
+     * Whether or not the local participant is sharing a video.
31
+     */
32
+    _sharingVideo: boolean
33
+};
34
+
35
+/**
36
+ * Implements an {@link AbstractButton} to open the user documentation in a new window.
37
+ */
38
+class SharedVideoButton extends AbstractButton<Props, *> {
39
+    accessibilityLabel = 'toolbar.accessibilityLabel.sharedvideo';
40
+    icon = IconShareVideo;
41
+    label = 'toolbar.sharedvideo';
42
+    tooltip = 'toolbar.sharedvideo';
43
+    toggledLabel = 'toolbar.stopSharedVideo';
44
+
45
+    /**
46
+     * Handles clicking / pressing the button, and opens a new dialog.
47
+     *
48
+     * @private
49
+     * @returns {void}
50
+     */
51
+    _handleClick() {
52
+        this._doToggleSharedVideoDialog();
53
+    }
54
+
55
+    /**
56
+     * Indicates whether this button is in toggled state or not.
57
+     *
58
+     * @override
59
+     * @protected
60
+     * @returns {boolean}
61
+     */
62
+    _isToggled() {
63
+        return this.props._sharingVideo;
64
+    }
65
+
66
+    /**
67
+     * Indicates whether this button is disabled or not.
68
+     *
69
+     * @override
70
+     * @protected
71
+     * @returns {boolean}
72
+     */
73
+    _isDisabled() {
74
+        return this.props._isDisabled;
75
+    }
76
+
77
+    /**
78
+     * Dispatches an action to toggle video sharing.
79
+     *
80
+     * @private
81
+     * @returns {void}
82
+     */
83
+    _doToggleSharedVideoDialog() {
84
+        const { dispatch } = this.props;
85
+
86
+        return this._isToggled()
87
+            ? APP.UI.stopSharedVideoEmitter()
88
+            : dispatch(showSharedVideoDialog(id => APP.UI.startSharedVideoEmitter(id)));
89
+    }
90
+}
91
+
92
+/**
93
+ * Maps part of the Redux state to the props of this component.
94
+ *
95
+ * @param {Object} state - The Redux state.
96
+ * @private
97
+ * @returns {Props}
98
+ */
99
+function _mapStateToProps(state): Object {
100
+    const {
101
+        disabled: sharedVideoBtnDisabled,
102
+        status: sharedVideoStatus
103
+    } = state['features/shared-video'];
104
+
105
+    return {
106
+        _isDisabled: sharedVideoBtnDisabled,
107
+        _sharingVideo: isSharingStatus(sharedVideoStatus)
108
+    };
109
+}
110
+
111
+
112
+export default translate(connect(_mapStateToProps)(SharedVideoButton));

+ 102
- 0
react/features/shared-video/components/web/SharedVideoDialog.js 查看文件

@@ -0,0 +1,102 @@
1
+// @flow
2
+
3
+import { FieldTextStateless } from '@atlaskit/field-text';
4
+import React from 'react';
5
+
6
+import { Dialog } from '../../../base/dialog';
7
+import { translate } from '../../../base/i18n';
8
+import { getFieldValue } from '../../../base/react';
9
+import { connect } from '../../../base/redux';
10
+import { defaultSharedVideoLink } from '../../constants';
11
+import { getYoutubeLink } from '../../functions';
12
+import AbstractSharedVideoDialog from '../AbstractSharedVideoDialog';
13
+
14
+/**
15
+ * Component that renders the video share dialog.
16
+ *
17
+ * @returns {React$Element<any>}
18
+ */
19
+class SharedVideoDialog extends AbstractSharedVideoDialog<*> {
20
+
21
+    /**
22
+     * Instantiates a new component.
23
+     *
24
+     * @inheritdoc
25
+     */
26
+    constructor(props) {
27
+        super(props);
28
+
29
+        this.state = {
30
+            value: '',
31
+            okDisabled: true
32
+        };
33
+
34
+        this._onChange = this._onChange.bind(this);
35
+        this._onSubmitValue = this._onSubmitValue.bind(this);
36
+    }
37
+
38
+    _onChange: Object => void;
39
+
40
+    /**
41
+     * Callback for the onChange event of the field.
42
+     *
43
+     * @param {Object} evt - The static event.
44
+     * @returns {void}
45
+     */
46
+    _onChange(evt: Object) {
47
+        const linkValue = getFieldValue(evt);
48
+
49
+        this.setState({
50
+            value: linkValue,
51
+            okDisabled: !getYoutubeLink(linkValue)
52
+        });
53
+    }
54
+
55
+    _onSubmitValue: () => boolean;
56
+
57
+    /**
58
+     * Callback to be invoked when the value of the link input is submitted.
59
+     *
60
+     * @returns {boolean}
61
+     */
62
+    _onSubmitValue() {
63
+        return this._onSetVideoLink(this.state.value);
64
+    }
65
+
66
+    /**
67
+     * Implements React's {@link Component#render()}.
68
+     *
69
+     * @inheritdoc
70
+     */
71
+    render() {
72
+        const { t } = this.props;
73
+
74
+        return (
75
+            <Dialog
76
+                hideCancelButton = { false }
77
+                okDisabled = { this.state.okDisabled }
78
+                okKey = { t('dialog.Share') }
79
+                onSubmit = { this._onSubmitValue }
80
+                titleKey = { t('dialog.shareVideoTitle') }
81
+                width = { 'small' }>
82
+                <FieldTextStateless
83
+                    autoFocus = { true }
84
+                    className = 'input-control'
85
+                    compact = { false }
86
+                    label = { t('dialog.videoLink') }
87
+                    name = 'sharedVideoUrl'
88
+                    onChange = { this._onChange }
89
+                    placeholder = { defaultSharedVideoLink }
90
+                    shouldFitContainer = { true }
91
+                    type = 'text'
92
+                    value = { this.state.value } />
93
+            </Dialog>
94
+        );
95
+    }
96
+
97
+    _onSetVideoLink: string => boolean;
98
+
99
+    _onChange: Object => void;
100
+}
101
+
102
+export default translate(connect()(SharedVideoDialog));

+ 5
- 0
react/features/shared-video/components/web/index.js 查看文件

@@ -0,0 +1,5 @@
1
+// @flow
2
+
3
+export { default as SharedVideoButton } from './SharedVideoButton';
4
+export { default as SharedVideoDialog } from './SharedVideoDialog';
5
+

+ 19
- 0
react/features/shared-video/constants.js 查看文件

@@ -0,0 +1,19 @@
1
+// @flow
2
+
3
+/**
4
+ * Example shared video link.
5
+ * @type {string}
6
+ */
7
+export const defaultSharedVideoLink = 'https://youtu.be/TB7LlM4erx8';
8
+
9
+/**
10
+ * Fixed name of the video player fake participant.
11
+ * @type {string}
12
+ */
13
+export const VIDEO_PLAYER_PARTICIPANT_NAME = 'YouTube';
14
+
15
+/**
16
+ * Shared video command.
17
+ * @type {string}
18
+ */
19
+export const SHARED_VIDEO = 'shared-video';

+ 44
- 0
react/features/shared-video/functions.js 查看文件

@@ -0,0 +1,44 @@
1
+// @flow
2
+
3
+import { getParticipants } from '../base/participants';
4
+
5
+import { VIDEO_PLAYER_PARTICIPANT_NAME } from './constants';
6
+
7
+/**
8
+ * Validates the entered video url.
9
+ *
10
+ * It returns a boolean to reflect whether the url matches the youtube regex.
11
+ *
12
+ * @param {string} url - The entered video link.
13
+ * @returns {boolean}
14
+ */
15
+export function getYoutubeLink(url: string) {
16
+    const p = /^(?:https?:\/\/)?(?:www\.)?(?:youtu\.be\/|(?:m\.)?youtube\.com\/(?:embed\/|v\/|watch\?v=|watch\?.+&v=))((\w|-){11})(?:\S+)?$/;// eslint-disable-line max-len
17
+    const result = url.match(p);
18
+
19
+    return result ? result[1] : false;
20
+}
21
+
22
+
23
+/**
24
+ * Checks if the status is one that is actually sharing the video - playing, pause or start.
25
+ *
26
+ * @param {string} status - The shared video status.
27
+ * @returns {boolean}
28
+ */
29
+export function isSharingStatus(status: string) {
30
+    return [ 'playing', 'pause', 'start' ].includes(status);
31
+}
32
+
33
+
34
+/**
35
+ * Returns true if there is a video being shared in the meeting.
36
+ *
37
+ * @param {Object | Function} stateful - The Redux state or a function that gets resolved to the Redux state.
38
+ * @returns {boolean}
39
+ */
40
+export function isVideoPlaying(stateful: Object | Function): boolean {
41
+    return Boolean(getParticipants(stateful).find(p => p.isFakeParticipant
42
+        && p.name === VIDEO_PLAYER_PARTICIPANT_NAME)
43
+    );
44
+}

+ 0
- 2
react/features/shared-video/index.js 查看文件

@@ -1,2 +0,0 @@
1
-export * from './actions';
2
-export * from './actionTypes';

+ 0
- 30
react/features/shared-video/middleware.js 查看文件

@@ -1,30 +0,0 @@
1
-// @flow
2
-
3
-import UIEvents from '../../../service/UI/UIEvents';
4
-import { MiddlewareRegistry } from '../base/redux';
5
-
6
-import { TOGGLE_SHARED_VIDEO } from './actionTypes';
7
-
8
-declare var APP: Object;
9
-
10
-/**
11
- * Middleware that captures actions related to YouTube video sharing and updates
12
- * components not hooked into redux.
13
- *
14
- * @param {Store} store - The redux store.
15
- * @returns {Function}
16
- */
17
-// eslint-disable-next-line no-unused-vars
18
-MiddlewareRegistry.register(store => next => action => {
19
-    if (typeof APP === 'undefined') {
20
-        return next(action);
21
-    }
22
-
23
-    switch (action.type) {
24
-    case TOGGLE_SHARED_VIDEO:
25
-        APP.UI.emitEvent(UIEvents.SHARED_VIDEO_CLICKED);
26
-        break;
27
-    }
28
-
29
-    return next(action);
30
-});

react/features/youtube-player/middleware.js → react/features/shared-video/middleware.native.js 查看文件

@@ -11,13 +11,12 @@ import {
11 11
 import { MiddlewareRegistry, StateListenerRegistry } from '../base/redux';
12 12
 
13 13
 import { TOGGLE_SHARED_VIDEO, SET_SHARED_VIDEO_STATUS } from './actionTypes';
14
-import { setSharedVideoStatus, showEnterVideoLinkPrompt } from './actions';
15
-import { YOUTUBE_PARTICIPANT_NAME } from './constants';
16
-
17
-const SHARED_VIDEO = 'shared-video';
14
+import { setSharedVideoStatus, showSharedVideoDialog } from './actions.native';
15
+import { SHARED_VIDEO, VIDEO_PLAYER_PARTICIPANT_NAME } from './constants';
16
+import { isSharingStatus } from './functions';
18 17
 
19 18
 /**
20
- * Middleware that captures actions related to YouTube video sharing and updates
19
+ * Middleware that captures actions related to video sharing and updates
21 20
  * components not hooked into redux.
22 21
  *
23 22
  * @param {Store} store - The redux store.
@@ -29,7 +28,7 @@ MiddlewareRegistry.register(store => next => action => {
29 28
     const conference = getCurrentConference(state);
30 29
     const localParticipantId = getLocalParticipant(state)?.id;
31 30
     const { videoId, status, ownerId, time } = action;
32
-    const { ownerId: stateOwnerId, videoId: stateVideoId } = state['features/youtube-player'];
31
+    const { ownerId: stateOwnerId, videoId: stateVideoId } = state['features/shared-video'];
33 32
 
34 33
     switch (action.type) {
35 34
     case TOGGLE_SHARED_VIDEO:
@@ -71,7 +70,7 @@ StateListenerRegistry.register(
71 70
                     const localParticipantId = getLocalParticipant(getState()).id;
72 71
                     const status = attributes.state;
73 72
 
74
-                    if ([ 'playing', 'pause', 'start' ].includes(status)) {
73
+                    if (isSharingStatus(status)) {
75 74
                         handleSharingVideoStatus(store, value, attributes, conference);
76 75
                     } else if (status === 'stop') {
77 76
                         dispatch(participantLeft(value, conference));
@@ -82,7 +81,8 @@ StateListenerRegistry.register(
82 81
                 }
83 82
             );
84 83
         }
85
-    });
84
+    }
85
+);
86 86
 
87 87
 /**
88 88
  * Handles the playing, pause and start statuses for the shared video.
@@ -90,7 +90,7 @@ StateListenerRegistry.register(
90 90
  * Sets the SharedVideoStatus if the event was triggered by the local user.
91 91
  *
92 92
  * @param {Store} store - The redux store.
93
- * @param {string} videoId - The YoutubeId of the video to the shared.
93
+ * @param {string} videoId - The id of the video to the shared.
94 94
  * @param {Object} attributes - The attributes received from the share video command.
95 95
  * @param {JitsiConference} conference - The current conference.
96 96
  * @returns {void}
@@ -98,7 +98,7 @@ StateListenerRegistry.register(
98 98
 function handleSharingVideoStatus(store, videoId, { state, time, from }, conference) {
99 99
     const { dispatch, getState } = store;
100 100
     const localParticipantId = getLocalParticipant(getState()).id;
101
-    const oldStatus = getState()['features/youtube-player']?.status;
101
+    const oldStatus = getState()['features/shared-video']?.status;
102 102
 
103 103
     if (state === 'start' || ![ 'playing', 'pause', 'start' ].includes(oldStatus)) {
104 104
         dispatch(participantJoined({
@@ -106,7 +106,7 @@ function handleSharingVideoStatus(store, videoId, { state, time, from }, confere
106 106
             id: videoId,
107 107
             isFakeParticipant: true,
108 108
             avatarURL: `https://img.youtube.com/vi/${videoId}/0.jpg`,
109
-            name: YOUTUBE_PARTICIPANT_NAME
109
+            name: VIDEO_PLAYER_PARTICIPANT_NAME
110 110
         }));
111 111
 
112 112
         dispatch(pinParticipant(videoId));
@@ -130,7 +130,7 @@ function handleSharingVideoStatus(store, videoId, { state, time, from }, confere
130 130
 function _toggleSharedVideo(store, next, action) {
131 131
     const { dispatch, getState } = store;
132 132
     const state = getState();
133
-    const { videoId, ownerId, status } = state['features/youtube-player'];
133
+    const { videoId, ownerId, status } = state['features/shared-video'];
134 134
     const localParticipant = getLocalParticipant(state);
135 135
 
136 136
     if (status === 'playing' || status === 'start' || status === 'pause') {
@@ -138,7 +138,7 @@ function _toggleSharedVideo(store, next, action) {
138 138
             dispatch(setSharedVideoStatus(videoId, 'stop', 0, localParticipant.id));
139 139
         }
140 140
     } else {
141
-        dispatch(showEnterVideoLinkPrompt(id => _onVideoLinkEntered(store, id)));
141
+        dispatch(showSharedVideoDialog(id => _onVideoLinkEntered(store, id)));
142 142
     }
143 143
 
144 144
     return next(action);
@@ -148,7 +148,7 @@ function _toggleSharedVideo(store, next, action) {
148 148
  * Sends SHARED_VIDEO start command.
149 149
  *
150 150
  * @param {Store} store - The redux store.
151
- * @param {string} id - The youtube id of the video to be shared.
151
+ * @param {string} id - The id of the video to be shared.
152 152
  * @returns {void}
153 153
  */
154 154
 function _onVideoLinkEntered(store, id) {
@@ -167,7 +167,7 @@ function _onVideoLinkEntered(store, id) {
167 167
 /**
168 168
  * Sends SHARED_VIDEO command.
169 169
  *
170
- * @param {string} id - The youtube id of the video.
170
+ * @param {string} id - The id of the video.
171 171
  * @param {string} status - The status of the shared video.
172 172
  * @param {JitsiConference} conference - The current conference.
173 173
  * @param {string} localParticipantId - The id of the local participant.

+ 63
- 0
react/features/shared-video/middleware.web.js 查看文件

@@ -0,0 +1,63 @@
1
+// @flow
2
+
3
+import UIEvents from '../../../service/UI/UIEvents';
4
+import { getCurrentConference } from '../base/conference';
5
+import { getLocalParticipant } from '../base/participants';
6
+import { MiddlewareRegistry, StateListenerRegistry } from '../base/redux';
7
+
8
+import { TOGGLE_SHARED_VIDEO } from './actionTypes';
9
+import { setDisableButton } from './actions.web';
10
+import { SHARED_VIDEO } from './constants';
11
+
12
+declare var APP: Object;
13
+
14
+/**
15
+ * Middleware that captures actions related to video sharing and updates
16
+ * components not hooked into redux.
17
+ *
18
+ * @param {Store} store - The redux store.
19
+ * @returns {Function}
20
+ */
21
+// eslint-disable-next-line no-unused-vars
22
+MiddlewareRegistry.register(store => next => action => {
23
+    if (typeof APP === 'undefined') {
24
+        return next(action);
25
+    }
26
+
27
+    switch (action.type) {
28
+    case TOGGLE_SHARED_VIDEO:
29
+        APP.UI.emitEvent(UIEvents.SHARED_VIDEO_CLICKED);
30
+        break;
31
+    }
32
+
33
+    return next(action);
34
+});
35
+
36
+/**
37
+ * Set up state change listener to disable or enable the share video button in
38
+ * the toolbar menu.
39
+ */
40
+StateListenerRegistry.register(
41
+    state => getCurrentConference(state),
42
+    (conference, store, previousConference) => {
43
+        if (conference && conference !== previousConference) {
44
+            conference.addCommandListener(SHARED_VIDEO,
45
+                ({ attributes }) => {
46
+
47
+                    const { dispatch, getState } = store;
48
+                    const { from } = attributes;
49
+                    const localParticipantId = getLocalParticipant(getState()).id;
50
+                    const status = attributes.state;
51
+
52
+                    if (status === 'playing') {
53
+                        if (localParticipantId !== from) {
54
+                            dispatch(setDisableButton(true));
55
+                        }
56
+                    } else if (status === 'stop') {
57
+                        dispatch(setDisableButton(false));
58
+                    }
59
+                }
60
+            );
61
+        }
62
+    }
63
+);

react/features/shared-video/reducer.js → react/features/shared-video/reducer.native.js 查看文件

@@ -1,3 +1,5 @@
1
+// @flow
2
+
1 3
 import { ReducerRegistry } from '../base/redux';
2 4
 
3 5
 import { SET_SHARED_VIDEO_STATUS } from './actionTypes';
@@ -6,13 +8,17 @@ import { SET_SHARED_VIDEO_STATUS } from './actionTypes';
6 8
  * Reduces the Redux actions of the feature features/shared-video.
7 9
  */
8 10
 ReducerRegistry.register('features/shared-video', (state = {}, action) => {
11
+    const { videoId, status, time, ownerId } = action;
12
+
9 13
     switch (action.type) {
10 14
     case SET_SHARED_VIDEO_STATUS:
11 15
         return {
12 16
             ...state,
13
-            status: action.status
17
+            videoId,
18
+            status,
19
+            time,
20
+            ownerId
14 21
         };
15
-
16 22
     default:
17 23
         return state;
18 24
     }

+ 29
- 0
react/features/shared-video/reducer.web.js 查看文件

@@ -0,0 +1,29 @@
1
+// @flow
2
+
3
+import { ReducerRegistry } from '../base/redux';
4
+
5
+import { SET_SHARED_VIDEO_STATUS, SET_DISABLE_BUTTON } from './actionTypes';
6
+
7
+/**
8
+ * Reduces the Redux actions of the feature features/shared-video.
9
+ */
10
+ReducerRegistry.register('features/shared-video', (state = {}, action) => {
11
+    const { status, disabled } = action;
12
+
13
+    switch (action.type) {
14
+    case SET_SHARED_VIDEO_STATUS:
15
+        return {
16
+            ...state,
17
+            status
18
+        };
19
+
20
+    case SET_DISABLE_BUTTON:
21
+        return {
22
+            ...state,
23
+            disabled
24
+        };
25
+
26
+    default:
27
+        return state;
28
+    }
29
+});

+ 2
- 2
react/features/toolbox/components/native/OverflowMenu.js 查看文件

@@ -15,9 +15,9 @@ import { LobbyModeButton } from '../../../lobby/components/native';
15 15
 import { AudioRouteButton } from '../../../mobile/audio-mode';
16 16
 import { LiveStreamButton, RecordButton } from '../../../recording';
17 17
 import { RoomLockButton } from '../../../room-lock';
18
+import { SharedVideoButton } from '../../../shared-video/components';
18 19
 import { ClosedCaptionButton } from '../../../subtitles';
19 20
 import { TileViewButton } from '../../../video-layout';
20
-import { VideoShareButton } from '../../../youtube-player/components';
21 21
 import HelpButton from '../HelpButton';
22 22
 import MuteEveryoneButton from '../MuteEveryoneButton';
23 23
 
@@ -140,7 +140,7 @@ class OverflowMenu extends PureComponent<Props, State> {
140 140
                     <TileViewButton { ...buttonProps } />
141 141
                     <RecordButton { ...buttonProps } />
142 142
                     <LiveStreamButton { ...buttonProps } />
143
-                    <VideoShareButton { ...buttonProps } />
143
+                    <SharedVideoButton { ...buttonProps } />
144 144
                     <RoomLockButton { ...buttonProps } />
145 145
                     <ClosedCaptionButton { ...buttonProps } />
146 146
                     <SharedDocumentButton { ...buttonProps } />

+ 6
- 43
react/features/toolbox/components/web/Toolbox.js 查看文件

@@ -22,8 +22,7 @@ import {
22 22
     IconPresentation,
23 23
     IconRaisedHand,
24 24
     IconRec,
25
-    IconShareDesktop,
26
-    IconShareVideo
25
+    IconShareDesktop
27 26
 } from '../../../base/icons';
28 27
 import JitsiMeetJS from '../../../base/lib-jitsi-meet';
29 28
 import {
@@ -57,7 +56,7 @@ import {
57 56
     SettingsButton,
58 57
     openSettingsDialog
59 58
 } from '../../../settings';
60
-import { toggleSharedVideo } from '../../../shared-video';
59
+import { SharedVideoButton } from '../../../shared-video/components';
61 60
 import { SpeakerStats } from '../../../speaker-stats';
62 61
 import {
63 62
     ClosedCaptionButton
@@ -90,6 +89,7 @@ import OverflowMenuProfileItem from './OverflowMenuProfileItem';
90 89
 import ToolbarButton from './ToolbarButton';
91 90
 import VideoSettingsButton from './VideoSettingsButton';
92 91
 
92
+
93 93
 /**
94 94
  * The type of the React {@code Component} props of {@link Toolbox}.
95 95
  */
@@ -259,7 +259,6 @@ class Toolbox extends Component<Props, State> {
259 259
         this._onToolbarToggleProfile = this._onToolbarToggleProfile.bind(this);
260 260
         this._onToolbarToggleRaiseHand = this._onToolbarToggleRaiseHand.bind(this);
261 261
         this._onToolbarToggleScreenshare = this._onToolbarToggleScreenshare.bind(this);
262
-        this._onToolbarToggleSharedVideo = this._onToolbarToggleSharedVideo.bind(this);
263 262
         this._onToolbarOpenLocalRecordingInfoDialog = this._onToolbarOpenLocalRecordingInfoDialog.bind(this);
264 263
         this._onShortcutToggleTileView = this._onShortcutToggleTileView.bind(this);
265 264
 
@@ -504,16 +503,6 @@ class Toolbox extends Component<Props, State> {
504 503
         }
505 504
     }
506 505
 
507
-    /**
508
-     * Dispatches an action to toggle YouTube video sharing.
509
-     *
510
-     * @private
511
-     * @returns {void}
512
-     */
513
-    _doToggleSharedVideo() {
514
-        this.props.dispatch(toggleSharedVideo());
515
-    }
516
-
517 506
     /**
518 507
      * Dispatches an action to toggle the video quality dialog.
519 508
      *
@@ -897,24 +886,6 @@ class Toolbox extends Component<Props, State> {
897 886
         this._doToggleScreenshare();
898 887
     }
899 888
 
900
-    _onToolbarToggleSharedVideo: () => void;
901
-
902
-    /**
903
-     * Creates an analytics toolbar event and dispatches an action for toggling
904
-     * the sharing of a YouTube video.
905
-     *
906
-     * @private
907
-     * @returns {void}
908
-     */
909
-    _onToolbarToggleSharedVideo() {
910
-        sendAnalytics(createToolbarEvent('shared.video.toggled',
911
-            {
912
-                enable: !this.props._sharingVideo
913
-            }));
914
-
915
-        this._doToggleSharedVideo();
916
-    }
917
-
918 889
     _onToolbarOpenLocalRecordingInfoDialog: () => void;
919 890
 
920 891
     /**
@@ -930,7 +901,7 @@ class Toolbox extends Component<Props, State> {
930 901
     }
931 902
 
932 903
     /**
933
-     * Returns true if the the desktop sharing button should be visible and
904
+     * Returns true if the desktop sharing button should be visible and
934 905
      * false otherwise.
935 906
      *
936 907
      * @returns {boolean}
@@ -1028,7 +999,6 @@ class Toolbox extends Component<Props, State> {
1028 999
             _feedbackConfigured,
1029 1000
             _fullScreen,
1030 1001
             _screensharing,
1031
-            _sharingVideo,
1032 1002
             t
1033 1003
         } = this.props;
1034 1004
 
@@ -1057,12 +1027,9 @@ class Toolbox extends Component<Props, State> {
1057 1027
                     key = 'record'
1058 1028
                     showLabel = { true } />,
1059 1029
             this._shouldShowButton('sharedvideo')
1060
-                && <OverflowMenuItem
1061
-                    accessibilityLabel = { t('toolbar.accessibilityLabel.sharedvideo') }
1062
-                    icon = { IconShareVideo }
1030
+                && <SharedVideoButton
1063 1031
                     key = 'sharedvideo'
1064
-                    onClick = { this._onToolbarToggleSharedVideo }
1065
-                    text = { _sharingVideo ? t('toolbar.stopSharedVideo') : t('toolbar.sharedvideo') } />,
1032
+                    showLabel = { true } />,
1066 1033
             this._shouldShowButton('etherpad')
1067 1034
                 && <SharedDocumentButton
1068 1035
                     key = 'etherpad'
@@ -1435,7 +1402,6 @@ function _mapStateToProps(state) {
1435 1402
         callStatsID,
1436 1403
         enableFeaturesBasedOnToken
1437 1404
     } = state['features/base/config'];
1438
-    const sharedVideoStatus = state['features/shared-video'].status;
1439 1405
     const {
1440 1406
         fullScreen,
1441 1407
         overflowMenuVisible
@@ -1476,9 +1442,6 @@ function _mapStateToProps(state) {
1476 1442
         _overflowMenuVisible: overflowMenuVisible,
1477 1443
         _raisedHand: localParticipant.raisedHand,
1478 1444
         _screensharing: localVideo && localVideo.videoType === 'desktop',
1479
-        _sharingVideo: sharedVideoStatus === 'playing'
1480
-            || sharedVideoStatus === 'start'
1481
-            || sharedVideoStatus === 'pause',
1482 1445
         _visible: isToolboxVisible(state),
1483 1446
         _visibleButtons: equals(visibleButtons, buttons) ? visibleButtons : buttons
1484 1447
     };

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

@@ -10,7 +10,7 @@ import {
10 10
     SINGLE_COLUMN_BREAKPOINT,
11 11
     TWO_COLUMN_BREAKPOINT
12 12
 } from '../filmstrip/constants';
13
-import { isYoutubeVideoPlaying } from '../youtube-player/functions';
13
+import { isVideoPlaying } from '../shared-video/functions';
14 14
 
15 15
 import { LAYOUTS } from './constants';
16 16
 
@@ -154,7 +154,7 @@ export function shouldDisplayTileView(state: Object = {}) {
154 154
         || participantCount < 3
155 155
 
156 156
         // There is a shared YouTube video in the meeting
157
-        || isYoutubeVideoPlaying(state)
157
+        || isVideoPlaying(state)
158 158
 
159 159
         // We want jibri to use stage view by default
160 160
         || iAmRecorder

+ 0
- 22
react/features/youtube-player/actionTypes.js 查看文件

@@ -1,22 +0,0 @@
1
-/**
2
- * The type of the action which signals to update the current known state of the
3
- * shared YouTube video.
4
- *
5
- * {
6
- *     type: SET_SHARED_VIDEO_STATUS,
7
- *     status: string,
8
- *     time: string,
9
- *     ownerId: string
10
- * }
11
- */
12
-export const SET_SHARED_VIDEO_STATUS = 'SET_SHARED_VIDEO_STATUS';
13
-
14
-/**
15
- * The type of the action which signals to start the flow for starting or
16
- * stopping a shared YouTube video.
17
- *
18
- * {
19
- *     type: TOGGLE_SHARED_VIDEO
20
- * }
21
- */
22
-export const TOGGLE_SHARED_VIDEO = 'TOGGLE_SHARED_VIDEO';

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

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

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

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

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

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

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

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

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

@@ -1,15 +0,0 @@
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
-}

+ 0
- 24
react/features/youtube-player/reducer.js 查看文件

@@ -1,24 +0,0 @@
1
-// @flow
2
-import { ReducerRegistry } from '../base/redux';
3
-
4
-import { SET_SHARED_VIDEO_STATUS } from './actionTypes';
5
-
6
-/**
7
- * Reduces the Redux actions of the feature features/youtube-player.
8
- */
9
-ReducerRegistry.register('features/youtube-player', (state = {}, action) => {
10
-    const { videoId, status, time, ownerId } = action;
11
-
12
-    switch (action.type) {
13
-    case SET_SHARED_VIDEO_STATUS:
14
-        return {
15
-            ...state,
16
-            videoId,
17
-            status,
18
-            time,
19
-            ownerId
20
-        };
21
-    default:
22
-        return state;
23
-    }
24
-});

正在加载...
取消
保存