ソースを参照

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.
master
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
-});

読み込み中…
キャンセル
保存