Selaa lähdekoodia

Updates recording dialog. (#3953)

* Updates recording dialog.

* Update config.js doc.

* Adds comment and make a check more intuitive.

* Changes of using enum for recording types.
master
Дамян Минков 6 vuotta sitten
vanhempi
commit
12d0aef686

+ 5
- 0
config.js Näytä tiedosto

180
     //     redirectURI:
180
     //     redirectURI:
181
     //          'https://jitsi-meet.example.com/subfolder/static/oauth.html'
181
     //          'https://jitsi-meet.example.com/subfolder/static/oauth.html'
182
     // },
182
     // },
183
+    // When integrations like dropbox are enabled only that will be shown,
184
+    // by enabling fileRecordingsServiceEnabled, we show both the integrations
185
+    // and the generic recording service (its configuration and storage type
186
+    // depends on jibri configuration)
187
+    // fileRecordingsServiceEnabled: false
183
 
188
 
184
     // Whether to enable live streaming or not.
189
     // Whether to enable live streaming or not.
185
     // liveStreamingEnabled: false,
190
     // liveStreamingEnabled: false,

+ 20
- 25
css/_recording.scss Näytä tiedosto

11
         flex: 0;
11
         flex: 0;
12
         flex-direction: row;
12
         flex-direction: row;
13
         justify-content: space-between;
13
         justify-content: space-between;
14
-        align-items: center;
14
+        margin-top: 32px;
15
 
15
 
16
         .recording-title {
16
         .recording-title {
17
+            display: inline-flex;
18
+            align-items: center;
17
             font-size: 16px;
19
             font-size: 16px;
18
-            font-weight: bold;
20
+            margin-left: 16px;
19
         }
21
         }
20
     }
22
     }
21
 
23
 
24
+    .recording-icon-container {
25
+        display: inline-flex;
26
+        align-items: center;
27
+    }
28
+
29
+    .recording-icon {
30
+        width: 32px;
31
+        height: 32px;
32
+        object-fit: contain;
33
+    }
34
+
35
+    .recording-switch {
36
+        margin-left: auto;
37
+    }
38
+
22
     .authorization-panel {
39
     .authorization-panel {
23
         display: flex;
40
         display: flex;
24
         flex-direction: column;
41
         flex-direction: column;
25
-        margin-bottom: 10px;
42
+        margin: 0 40px 10px 40px;
26
         padding-bottom: 10px;
43
         padding-bottom: 10px;
27
 
44
 
28
-        .dropbox-sign-in {
29
-            align-items: center;
30
-            border: 1px solid #4285f4;
31
-            background-color: white;
32
-            border-radius: 2px;
33
-            cursor: pointer;
34
-            display: inline-flex;
35
-            padding: 10px;
36
-            font-size: 18px;
37
-            font-weight: 600;
38
-            margin: 10px 0px;
39
-            color: #4285f4;
40
-
41
-            .dropbox-logo {
42
-                background-color: white;
43
-                border-radius: 2px;
44
-                display: inline-block;
45
-                padding-right: 5px;
46
-                height: 18px;
47
-            }
48
-        }
49
-
50
         .logged-in-panel {
45
         .logged-in-panel {
51
             padding: 10px;
46
             padding: 10px;
52
         }
47
         }

BIN
images/dropboxLogo_square.png Näytä tiedosto


BIN
images/jitsiLogo_square.png Näytä tiedosto


+ 3
- 2
lang/main.json Näytä tiedosto

518
         "on": "Recording",
518
         "on": "Recording",
519
         "pending": "Preparing to record the meeting...",
519
         "pending": "Preparing to record the meeting...",
520
         "rec": "REC",
520
         "rec": "REC",
521
+        "serviceDescription": "Your recording will be saved by the recording service",
521
         "serviceName": "Recording service",
522
         "serviceName": "Recording service",
522
-        "signIn": "sign in",
523
-        "signOut": "Sign Out",
523
+        "signIn": "Sign in",
524
+        "signOut": "Sign out",
524
         "startRecordingBody": "Are you sure you would like to start recording?",
525
         "startRecordingBody": "Are you sure you would like to start recording?",
525
         "unavailable": "Oops! The __serviceName__ is currently unavailable. We're working on resolving the issue. Please try again later.",
526
         "unavailable": "Oops! The __serviceName__ is currently unavailable. We're working on resolving the issue. Please try again later.",
526
         "unavailableTitle": "Recording unavailable"
527
         "unavailableTitle": "Recording unavailable"

+ 23
- 1
react/features/analytics/AnalyticsEvents.js Näytä tiedosto

338
  * @param {string} dialogName - The name of the dialog (e.g. 'start' or 'stop').
338
  * @param {string} dialogName - The name of the dialog (e.g. 'start' or 'stop').
339
  * @param {string} buttonName - The name of the button (e.g. 'confirm' or
339
  * @param {string} buttonName - The name of the button (e.g. 'confirm' or
340
  * 'cancel').
340
  * 'cancel').
341
+ * @param {Object} attributes - Attributes to attach to the event.
341
  * @returns {Object} The event in a format suitable for sending via
342
  * @returns {Object} The event in a format suitable for sending via
342
  * sendAnalytics.
343
  * sendAnalytics.
343
  */
344
  */
344
-export function createRecordingDialogEvent(dialogName, buttonName) {
345
+export function createRecordingDialogEvent(
346
+        dialogName, buttonName, attributes = {}) {
345
     return {
347
     return {
346
         action: 'clicked',
348
         action: 'clicked',
347
         actionSubject: buttonName,
349
         actionSubject: buttonName,
350
+        attributes,
348
         source: `${dialogName}.recording.dialog`,
351
         source: `${dialogName}.recording.dialog`,
349
         type: TYPE_UI
352
         type: TYPE_UI
350
     };
353
     };
351
 }
354
 }
352
 
355
 
356
+/**
357
+ * Creates an event which indicates that a specific button on one of the
358
+ * liveStreaming-related dialogs was clicked.
359
+ *
360
+ * @param {string} dialogName - The name of the dialog (e.g. 'start' or 'stop').
361
+ * @param {string} buttonName - The name of the button (e.g. 'confirm' or
362
+ * 'cancel').
363
+ * @returns {Object} The event in a format suitable for sending via
364
+ * sendAnalytics.
365
+ */
366
+export function createLiveStreamingDialogEvent(dialogName, buttonName) {
367
+    return {
368
+        action: 'clicked',
369
+        actionSubject: buttonName,
370
+        source: `${dialogName}.liveStreaming.dialog`,
371
+        type: TYPE_UI
372
+    };
373
+}
374
+
353
 /**
375
 /**
354
  * Creates an event which indicates that an action related to recording has
376
  * Creates an event which indicates that an action related to recording has
355
  * occured.
377
  * occured.

+ 44
- 0
react/features/base/react/components/native/Button.js Näytä tiedosto

1
+/* @flow */
2
+
3
+import React, { Component } from 'react';
4
+import { Text, TouchableOpacity } from 'react-native';
5
+
6
+type Props = {
7
+
8
+    /**
9
+     * React Elements to display within the component.
10
+     */
11
+    children: React$Node | Object,
12
+
13
+    /**
14
+     * Handler called when the user presses the button.
15
+     */
16
+    onValueChange: Function,
17
+
18
+    /**
19
+     * The component's external style
20
+     */
21
+    style: Object
22
+};
23
+
24
+/**
25
+ * Renders a button.
26
+ */
27
+export default class ButtonImpl extends Component<Props> {
28
+    /**
29
+     * Implements React's {@link Component#render()}, renders the button.
30
+     *
31
+     * @inheritdoc
32
+     * @returns {ReactElement}
33
+     */
34
+    render() {
35
+        return (
36
+            <TouchableOpacity
37
+                onPress = { this.props.onValueChange } >
38
+                <Text style = { this.props.style }>
39
+                    { this.props.children }
40
+                </Text>
41
+            </TouchableOpacity>
42
+        );
43
+    }
44
+}

+ 41
- 0
react/features/base/react/components/native/Image.js Näytä tiedosto

1
+// @flow
2
+
3
+import React, { Component } from 'react';
4
+import { Image } from 'react-native';
5
+
6
+/**
7
+ * The type of the React {@code Component} props of {@link Image}.
8
+ */
9
+type Props = {
10
+
11
+    /**
12
+     * The URL to be rendered as image.
13
+     */
14
+    src: string,
15
+
16
+    /**
17
+     * The component's external style
18
+     */
19
+    style: Object
20
+};
21
+
22
+/**
23
+ * A component rendering aN IMAGE.
24
+ *
25
+ * @extends Component
26
+ */
27
+export default class ImageImpl extends Component<Props> {
28
+    /**
29
+     * Implements React's {@link Component#render()}.
30
+     *
31
+     * @inheritdoc
32
+     * @returns {ReactElement}
33
+     */
34
+    render() {
35
+        return (
36
+            <Image
37
+                source = { this.props.src }
38
+                style = { this.props.style } />
39
+        );
40
+    }
41
+}

+ 2
- 0
react/features/base/react/components/native/index.js Näytä tiedosto

2
 
2
 
3
 export { default as AvatarListItem } from './AvatarListItem';
3
 export { default as AvatarListItem } from './AvatarListItem';
4
 export { default as BackButton } from './BackButton';
4
 export { default as BackButton } from './BackButton';
5
+export { default as Button } from './Button';
5
 export { default as Container } from './Container';
6
 export { default as Container } from './Container';
6
 export { default as ForwardButton } from './ForwardButton';
7
 export { default as ForwardButton } from './ForwardButton';
7
 export { default as Header } from './Header';
8
 export { default as Header } from './Header';
8
 export { default as HeaderLabel } from './HeaderLabel';
9
 export { default as HeaderLabel } from './HeaderLabel';
10
+export { default as Image } from './Image';
9
 export { default as Link } from './Link';
11
 export { default as Link } from './Link';
10
 export { default as LoadingIndicator } from './LoadingIndicator';
12
 export { default as LoadingIndicator } from './LoadingIndicator';
11
 export { default as Modal } from './Modal';
13
 export { default as Modal } from './Modal';

+ 41
- 0
react/features/base/react/components/web/Button.js Näytä tiedosto

1
+/* @flow */
2
+
3
+import Button from '@atlaskit/button';
4
+import React, { Component } from 'react';
5
+
6
+type Props = {
7
+
8
+    /**
9
+     * React Elements to display within the component.
10
+     */
11
+    children: React$Node | Object,
12
+
13
+    /**
14
+     * Handler called when the user presses the button.
15
+     */
16
+    onValueChange: Function
17
+};
18
+
19
+/**
20
+ * Renders a button.
21
+ */
22
+export default class ButtonImpl extends Component<Props> {
23
+    /**
24
+     * Implements React's {@link Component#render()}.
25
+     *
26
+     * @inheritdoc
27
+     * @returns {ReactElement}
28
+     */
29
+    render() {
30
+        const { onValueChange } = this.props;
31
+
32
+        return (
33
+            <Button
34
+                appearance = 'primary'
35
+                onClick = { onValueChange }
36
+                type = 'button'>
37
+                { this.props.children }
38
+            </Button>
39
+        );
40
+    }
41
+}

+ 19
- 0
react/features/base/react/components/web/Image.js Näytä tiedosto

1
+import React, { Component } from 'react';
2
+
3
+/**
4
+ * Implements a React/Web {@link Component} for displaying image
5
+ * in order to facilitate cross-platform source code.
6
+ *
7
+ * @extends Component
8
+ */
9
+export default class Image extends Component {
10
+    /**
11
+     * Implements React's {@link Component#render()}.
12
+     *
13
+     * @inheritdoc
14
+     * @returns {ReactElement}
15
+     */
16
+    render() {
17
+        return React.createElement('img', this.props);
18
+    }
19
+}

+ 2
- 0
react/features/base/react/components/web/index.js Näytä tiedosto

1
+export { default as Button } from './Button';
1
 export { default as Container } from './Container';
2
 export { default as Container } from './Container';
3
+export { default as Image } from './Image';
2
 export { default as LoadingIndicator } from './LoadingIndicator';
4
 export { default as LoadingIndicator } from './LoadingIndicator';
3
 export { default as MeetingsList } from './MeetingsList';
5
 export { default as MeetingsList } from './MeetingsList';
4
 export { default as MultiSelectAutocomplete } from './MultiSelectAutocomplete';
6
 export { default as MultiSelectAutocomplete } from './MultiSelectAutocomplete';

+ 5
- 2
react/features/dropbox/functions.native.js Näytä tiedosto

44
  * Returns <tt>true</tt> if the dropbox features is enabled and <tt>false</tt>
44
  * Returns <tt>true</tt> if the dropbox features is enabled and <tt>false</tt>
45
  * otherwise.
45
  * otherwise.
46
  *
46
  *
47
+ * @param {Object} state - The redux state.
47
  * @returns {boolean}
48
  * @returns {boolean}
48
  */
49
  */
49
-export function isEnabled() {
50
-    return Dropbox.ENABLED;
50
+export function isEnabled(state: Object) {
51
+    const { dropbox = {} } = state['features/base/config'];
52
+
53
+    return Dropbox.ENABLED && typeof dropbox.appKey === 'string';
51
 }
54
 }

+ 3
- 3
react/features/recording/components/LiveStream/AbstractStartLiveStreamDialog.js Näytä tiedosto

3
 import { Component } from 'react';
3
 import { Component } from 'react';
4
 
4
 
5
 import {
5
 import {
6
-    createRecordingDialogEvent,
6
+    createLiveStreamingDialogEvent,
7
     sendAnalytics
7
     sendAnalytics
8
 } from '../../../analytics';
8
 } from '../../../analytics';
9
 import { JitsiRecordingConstants } from '../../../base/lib-jitsi-meet';
9
 import { JitsiRecordingConstants } from '../../../base/lib-jitsi-meet';
149
      * @returns {boolean} True is returned to close the modal.
149
      * @returns {boolean} True is returned to close the modal.
150
      */
150
      */
151
     _onCancel() {
151
     _onCancel() {
152
-        sendAnalytics(createRecordingDialogEvent('start', 'cancel.button'));
152
+        sendAnalytics(createLiveStreamingDialogEvent('start', 'cancel.button'));
153
 
153
 
154
         return true;
154
         return true;
155
     }
155
     }
211
         }
211
         }
212
 
212
 
213
         sendAnalytics(
213
         sendAnalytics(
214
-            createRecordingDialogEvent('start', 'confirm.button'));
214
+            createLiveStreamingDialogEvent('start', 'confirm.button'));
215
 
215
 
216
         this.props._conference.startRecording({
216
         this.props._conference.startRecording({
217
             broadcastId: selectedBroadcastID,
217
             broadcastId: selectedBroadcastID,

+ 2
- 2
react/features/recording/components/LiveStream/AbstractStopLiveStreamDialog.js Näytä tiedosto

3
 import { Component } from 'react';
3
 import { Component } from 'react';
4
 
4
 
5
 import {
5
 import {
6
-    createRecordingDialogEvent,
6
+    createLiveStreamingDialogEvent,
7
     sendAnalytics
7
     sendAnalytics
8
 } from '../../../analytics';
8
 } from '../../../analytics';
9
 import { JitsiRecordingConstants } from '../../../base/lib-jitsi-meet';
9
 import { JitsiRecordingConstants } from '../../../base/lib-jitsi-meet';
61
      * @returns {boolean} True to close the modal.
61
      * @returns {boolean} True to close the modal.
62
      */
62
      */
63
     _onSubmit() {
63
     _onSubmit() {
64
-        sendAnalytics(createRecordingDialogEvent('stop', 'confirm.button'));
64
+        sendAnalytics(createLiveStreamingDialogEvent('stop', 'confirm.button'));
65
 
65
 
66
         const { _session } = this.props;
66
         const { _session } = this.props;
67
 
67
 

+ 22
- 6
react/features/recording/components/Recording/AbstractStartRecordingDialog.js Näytä tiedosto

11
     getDropboxData,
11
     getDropboxData,
12
     isEnabled as isDropboxEnabled
12
     isEnabled as isDropboxEnabled
13
 } from '../../../dropbox';
13
 } from '../../../dropbox';
14
+import { RECORDING_TYPES } from '../../constants';
14
 
15
 
15
 type Props = {
16
 type Props = {
16
 
17
 
24
      */
25
      */
25
     _appKey: string,
26
     _appKey: string,
26
 
27
 
28
+    /**
29
+     * Whether to show file recordings service, even if integrations
30
+     * are enabled.
31
+     */
32
+    _fileRecordingsServiceEnabled: boolean,
33
+
27
     /**
34
     /**
28
      * If true the dropbox integration is enabled, otherwise - disabled.
35
      * If true the dropbox integration is enabled, otherwise - disabled.
29
      */
36
      */
165
      * @returns {boolean} - True (to note that the modal should be closed).
172
      * @returns {boolean} - True (to note that the modal should be closed).
166
      */
173
      */
167
     _onSubmit() {
174
     _onSubmit() {
168
-        sendAnalytics(
169
-            createRecordingDialogEvent('start', 'confirm.button')
170
-        );
171
         const { _conference, _isDropboxEnabled, _token } = this.props;
175
         const { _conference, _isDropboxEnabled, _token } = this.props;
172
         let appData;
176
         let appData;
177
+        const attributes = {};
173
 
178
 
174
-        if (_isDropboxEnabled) {
179
+        if (_isDropboxEnabled && _token) {
175
             appData = JSON.stringify({
180
             appData = JSON.stringify({
176
                 'file_recording_metadata': {
181
                 'file_recording_metadata': {
177
                     'upload_credentials': {
182
                     'upload_credentials': {
178
-                        'service_name': 'dropbox',
183
+                        'service_name': RECORDING_TYPES.DROPBOX,
179
                         'token': _token
184
                         'token': _token
180
                     }
185
                     }
181
                 }
186
                 }
182
             });
187
             });
188
+            attributes.type = RECORDING_TYPES.DROPBOX;
189
+        } else {
190
+            attributes.type = RECORDING_TYPES.JITSI_REC_SERVICE;
183
         }
191
         }
184
 
192
 
193
+        sendAnalytics(
194
+            createRecordingDialogEvent('start', 'confirm.button', attributes)
195
+        );
196
+
185
         _conference.startRecording({
197
         _conference.startRecording({
186
             mode: JitsiRecordingConstants.mode.FILE,
198
             mode: JitsiRecordingConstants.mode.FILE,
187
             appData
199
             appData
212
  * }}
224
  * }}
213
  */
225
  */
214
 export function mapStateToProps(state: Object) {
226
 export function mapStateToProps(state: Object) {
215
-    const { dropbox = {} } = state['features/base/config'];
227
+    const {
228
+        fileRecordingsServiceEnabled = false,
229
+        dropbox = {}
230
+    } = state['features/base/config'];
216
 
231
 
217
     return {
232
     return {
218
         _appKey: dropbox.appKey,
233
         _appKey: dropbox.appKey,
219
         _conference: state['features/base/conference'].conference,
234
         _conference: state['features/base/conference'].conference,
235
+        _fileRecordingsServiceEnabled: fileRecordingsServiceEnabled,
220
         _isDropboxEnabled: isDropboxEnabled(state),
236
         _isDropboxEnabled: isDropboxEnabled(state),
221
         _token: state['features/dropbox'].token
237
         _token: state['features/dropbox'].token
222
     };
238
     };

+ 180
- 40
react/features/recording/components/Recording/StartRecordingDialogContent.js Näytä tiedosto

8
     sendAnalytics
8
     sendAnalytics
9
 } from '../../../analytics';
9
 } from '../../../analytics';
10
 import {
10
 import {
11
-    DialogContent,
12
     _abstractMapStateToProps
11
     _abstractMapStateToProps
13
 } from '../../../base/dialog';
12
 } from '../../../base/dialog';
14
 import { translate } from '../../../base/i18n';
13
 import { translate } from '../../../base/i18n';
15
 import {
14
 import {
15
+    Button,
16
     Container,
16
     Container,
17
+    Image,
17
     LoadingIndicator,
18
     LoadingIndicator,
18
     Switch,
19
     Switch,
19
     Text
20
     Text
20
 } from '../../../base/react';
21
 } from '../../../base/react';
21
-import { StyleType } from '../../../base/styles';
22
+import { ColorPalette, StyleType } from '../../../base/styles';
22
 import { authorizeDropbox, updateDropboxToken } from '../../../dropbox';
23
 import { authorizeDropbox, updateDropboxToken } from '../../../dropbox';
23
 
24
 
24
-import styles from './styles';
25
+import {
26
+    default as styles,
27
+    DROPBOX_LOGO,
28
+    JITSI_LOGO
29
+} from './styles';
30
+
31
+import { RECORDING_TYPES } from '../../constants';
25
 import { getRecordingDurationEstimation } from '../../functions';
32
 import { getRecordingDurationEstimation } from '../../functions';
26
 
33
 
27
 type Props = {
34
 type Props = {
36
      */
43
      */
37
     dispatch: Function,
44
     dispatch: Function,
38
 
45
 
46
+    /**
47
+     * Whether to show file recordings service, even if integrations
48
+     * are enabled.
49
+     */
50
+    fileRecordingsServiceEnabled: boolean,
51
+
39
     /**
52
     /**
40
      * If true the content related to the integrations will be shown.
53
      * If true the content related to the integrations will be shown.
41
      */
54
      */
67
     userName: ?string,
80
     userName: ?string,
68
 };
81
 };
69
 
82
 
83
+/**
84
+ * State of the component.
85
+ */
86
+type State = {
87
+
88
+    /**
89
+     * The currently selected recording service of type: RECORDING_TYPES.
90
+     */
91
+    selectedRecordingService: string
92
+};
93
+
94
+
70
 /**
95
 /**
71
  * React Component for getting confirmation to start a file recording session.
96
  * React Component for getting confirmation to start a file recording session.
72
  *
97
  *
73
  * @extends Component
98
  * @extends Component
74
  */
99
  */
75
-class StartRecordingDialogContent extends Component<Props> {
100
+class StartRecordingDialogContent extends Component<Props, State> {
76
     /**
101
     /**
77
      * Initializes a new {@code StartRecordingDialogContent} instance.
102
      * Initializes a new {@code StartRecordingDialogContent} instance.
78
      *
103
      *
82
         super(props);
107
         super(props);
83
 
108
 
84
         // Bind event handler so it is only bound once for every instance.
109
         // Bind event handler so it is only bound once for every instance.
85
-        this._signIn = this._signIn.bind(this);
86
-        this._signOut = this._signOut.bind(this);
87
-        this._onSwitchChange = this._onSwitchChange.bind(this);
110
+        this._onSignIn = this._onSignIn.bind(this);
111
+        this._onSignOut = this._onSignOut.bind(this);
112
+        this._onDropboxSwitchChange
113
+            = this._onDropboxSwitchChange.bind(this);
114
+        this._onRecordingServiceSwitchChange
115
+            = this._onRecordingServiceSwitchChange.bind(this);
116
+
117
+        // the initial state is jitsi rec service is always selected
118
+        // if only one type of recording is enabled this state will be ignored
119
+        this.state = {
120
+            selectedRecordingService: RECORDING_TYPES.JITSI_REC_SERVICE
121
+        };
88
     }
122
     }
89
 
123
 
90
     /**
124
     /**
94
      * @returns {React$Component}
128
      * @returns {React$Component}
95
      */
129
      */
96
     render() {
130
     render() {
97
-        if (this.props.integrationsEnabled === true) { // explicit true needed
98
-            return this._renderIntegrationsContent();
99
-        }
100
-
101
-        return this._renderNoIntegrationsContent();
131
+        return (
132
+            <Container
133
+                className = 'recording-dialog'
134
+                style = { styles.container }>
135
+                { this._renderNoIntegrationsContent() }
136
+                { this._renderIntegrationsContent() }
137
+            </Container>
138
+        );
102
     }
139
     }
103
 
140
 
104
     /**
141
     /**
107
      * @returns {React$Component}
144
      * @returns {React$Component}
108
      */
145
      */
109
     _renderNoIntegrationsContent() {
146
     _renderNoIntegrationsContent() {
147
+
148
+        // show the non integrations part only if fileRecordingsServiceEnabled
149
+        // is enabled or when there are no integrations enabled
150
+        if (!(this.props.fileRecordingsServiceEnabled
151
+            || !this.props.integrationsEnabled)) {
152
+            return null;
153
+        }
154
+
155
+        const { _dialogStyles, isValidating, t } = this.props;
156
+
157
+        const switchContent
158
+            = this.props.integrationsEnabled
159
+                ? (
160
+                    <Switch
161
+                        className = 'recording-switch'
162
+                        disabled = { isValidating }
163
+                        onValueChange
164
+                            = { this._onRecordingServiceSwitchChange }
165
+                        style = { styles.switch }
166
+                        trackColor = {{ false: ColorPalette.lightGrey }}
167
+                        value = {
168
+                            this.state.selectedRecordingService
169
+                                === RECORDING_TYPES.JITSI_REC_SERVICE } />
170
+                ) : null;
171
+
110
         return (
172
         return (
111
-            <DialogContent style = { this.props._dialogStyles.text }>
112
-                { this.props.t('recording.startRecordingBody') }
113
-            </DialogContent>
173
+            <Container
174
+                className = 'recording-header'
175
+                style = { styles.header }>
176
+                <Container className = 'recording-icon-container'>
177
+                    <Image
178
+                        className = 'recording-icon'
179
+                        src = { JITSI_LOGO }
180
+                        style = { styles.recordingIcon } />
181
+                </Container>
182
+                <Text
183
+                    className = 'recording-title'
184
+                    style = {{
185
+                        ..._dialogStyles.text,
186
+                        ...styles.title
187
+                    }}>
188
+                    { t('recording.serviceDescription') }
189
+                </Text>
190
+                { switchContent }
191
+            </Container>
114
         );
192
         );
115
     }
193
     }
116
 
194
 
121
      * @returns {React$Component}
199
      * @returns {React$Component}
122
      */
200
      */
123
     _renderIntegrationsContent() {
201
     _renderIntegrationsContent() {
202
+        if (!this.props.integrationsEnabled) {
203
+            return null;
204
+        }
205
+
124
         const { _dialogStyles, isTokenValid, isValidating, t } = this.props;
206
         const { _dialogStyles, isTokenValid, isValidating, t } = this.props;
125
 
207
 
126
         let content = null;
208
         let content = null;
209
+        let switchContent = null;
127
 
210
 
128
         if (isValidating) {
211
         if (isValidating) {
129
             content = this._renderSpinner();
212
             content = this._renderSpinner();
213
+            switchContent = <Container className = 'recording-switch' />;
130
         } else if (isTokenValid) {
214
         } else if (isTokenValid) {
131
             content = this._renderSignOut();
215
             content = this._renderSignOut();
216
+            switchContent = (
217
+                <Container className = 'recording-switch'>
218
+                    <Button
219
+                        onValueChange = { this._onSignOut }
220
+                        style = { styles.signButton }>
221
+                        { t('recording.signOut') }
222
+                    </Button>
223
+                </Container>
224
+            );
225
+
226
+        } else {
227
+            switchContent = (
228
+                <Container className = 'recording-switch'>
229
+                    <Button
230
+                        onValueChange = { this._onSignIn }
231
+                        style = { styles.signButton }>
232
+                        { t('recording.signIn') }
233
+                    </Button>
234
+                </Container>
235
+            );
132
         }
236
         }
133
 
237
 
134
-        // else { // Sign in screen:
135
-        // We don't need to render any additional information.
136
-        // }
238
+        if (this.props.fileRecordingsServiceEnabled) {
239
+            switchContent = (
240
+                <Switch
241
+                    className = 'recording-switch'
242
+                    disabled = { isValidating }
243
+                    onValueChange = { this._onDropboxSwitchChange }
244
+                    style = { styles.switch }
245
+                    trackColor = {{ false: ColorPalette.lightGrey }}
246
+                    value = { this.state.selectedRecordingService
247
+                        === RECORDING_TYPES.DROPBOX } />
248
+            );
249
+        }
137
 
250
 
138
         return (
251
         return (
139
-            <Container
140
-                className = 'recording-dialog'
141
-                style = { styles.container }>
252
+            <Container>
142
                 <Container
253
                 <Container
143
                     className = 'recording-header'
254
                     className = 'recording-header'
144
                     style = { styles.header }>
255
                     style = { styles.header }>
256
+                    <Container
257
+                        className = 'recording-icon-container'>
258
+                        <Image
259
+                            className = 'recording-icon'
260
+                            src = { DROPBOX_LOGO }
261
+                            style = { styles.recordingIcon } />
262
+                    </Container>
145
                     <Text
263
                     <Text
146
                         className = 'recording-title'
264
                         className = 'recording-title'
147
                         style = {{
265
                         style = {{
150
                         }}>
268
                         }}>
151
                         { t('recording.authDropboxText') }
269
                         { t('recording.authDropboxText') }
152
                     </Text>
270
                     </Text>
153
-                    <Switch
154
-                        disabled = { isValidating }
155
-                        onValueChange = { this._onSwitchChange }
156
-                        style = { styles.switch }
157
-                        value = { isTokenValid } />
271
+                    { switchContent }
158
                 </Container>
272
                 </Container>
159
                 <Container
273
                 <Container
160
                     className = 'authorization-panel'>
274
                     className = 'authorization-panel'>
164
         );
278
         );
165
     }
279
     }
166
 
280
 
167
-    _onSwitchChange: boolean => void;
281
+    _onDropboxSwitchChange: boolean => void;
282
+    _onRecordingServiceSwitchChange: boolean => void;
168
 
283
 
169
     /**
284
     /**
170
      * Handler for onValueChange events from the Switch component.
285
      * Handler for onValueChange events from the Switch component.
171
      *
286
      *
172
      * @returns {void}
287
      * @returns {void}
173
      */
288
      */
174
-    _onSwitchChange() {
289
+    _onRecordingServiceSwitchChange() {
290
+
291
+        // act like group, cannot toggle off
292
+        if (this.state.selectedRecordingService
293
+                === RECORDING_TYPES.JITSI_REC_SERVICE) {
294
+            return;
295
+        }
296
+
297
+        this.setState({
298
+            selectedRecordingService: RECORDING_TYPES.JITSI_REC_SERVICE
299
+        });
300
+
175
         if (this.props.isTokenValid) {
301
         if (this.props.isTokenValid) {
176
-            this._signOut();
177
-        } else {
178
-            this._signIn();
302
+            this._onSignOut();
303
+        }
304
+    }
305
+
306
+    /**
307
+     * Handler for onValueChange events from the Switch component.
308
+     *
309
+     * @returns {void}
310
+     */
311
+    _onDropboxSwitchChange() {
312
+        // act like group, cannot toggle off
313
+        if (this.state.selectedRecordingService
314
+                === RECORDING_TYPES.DROPBOX) {
315
+            return;
316
+        }
317
+
318
+        this.setState({
319
+            selectedRecordingService: RECORDING_TYPES.DROPBOX
320
+        });
321
+
322
+        if (!this.props.isTokenValid) {
323
+            this._onSignIn();
179
         }
324
         }
180
     }
325
     }
181
 
326
 
222
                         </Text>
367
                         </Text>
223
                     </Container>
368
                     </Container>
224
                 </Container>
369
                 </Container>
225
-                <Container style = { styles.startRecordingText }>
226
-                    <Text style = { styles.text }>
227
-                        { t('recording.startRecordingBody') }
228
-                    </Text>
229
-                </Container>
230
             </Container>
370
             </Container>
231
         );
371
         );
232
     }
372
     }
233
 
373
 
234
-    _signIn: () => {};
374
+    _onSignIn: () => {};
235
 
375
 
236
     /**
376
     /**
237
      * Sings in a user.
377
      * Sings in a user.
238
      *
378
      *
239
      * @returns {void}
379
      * @returns {void}
240
      */
380
      */
241
-    _signIn() {
381
+    _onSignIn() {
242
         sendAnalytics(
382
         sendAnalytics(
243
             createRecordingDialogEvent('start', 'signIn.button')
383
             createRecordingDialogEvent('start', 'signIn.button')
244
         );
384
         );
245
         this.props.dispatch(authorizeDropbox());
385
         this.props.dispatch(authorizeDropbox());
246
     }
386
     }
247
 
387
 
248
-    _signOut: () => {};
388
+    _onSignOut: () => {};
249
 
389
 
250
     /**
390
     /**
251
      * Sings out an user from dropbox.
391
      * Sings out an user from dropbox.
252
      *
392
      *
253
      * @returns {void}
393
      * @returns {void}
254
      */
394
      */
255
-    _signOut() {
395
+    _onSignOut() {
256
         sendAnalytics(
396
         sendAnalytics(
257
             createRecordingDialogEvent('start', 'signOut.button')
397
             createRecordingDialogEvent('start', 'signOut.button')
258
         );
398
         );

+ 12
- 2
react/features/recording/components/Recording/native/StartRecordingDialog.js Näytä tiedosto

25
      */
25
      */
26
     render() {
26
     render() {
27
         const { isTokenValid, isValidating, spaceLeft, userName } = this.state;
27
         const { isTokenValid, isValidating, spaceLeft, userName } = this.state;
28
-        const { _isDropboxEnabled } = this.props;
28
+        const { _fileRecordingsServiceEnabled, _isDropboxEnabled } = this.props;
29
+
30
+        // disable ok button id recording service is shown only, when
31
+        // validating dropbox token, if that is not enabled we either always
32
+        // show the ok button or if just dropbox is enabled ok is available
33
+        // when there is token
34
+        const isOkDisabled
35
+            = _fileRecordingsServiceEnabled ? isValidating
36
+                : _isDropboxEnabled ? !isTokenValid : false;
29
 
37
 
30
         return (
38
         return (
31
             <CustomSubmitDialog
39
             <CustomSubmitDialog
32
-                okDisabled = { _isDropboxEnabled && !isTokenValid }
40
+                okDisabled = { isOkDisabled }
33
                 onSubmit = { this._onSubmit } >
41
                 onSubmit = { this._onSubmit } >
34
                 <StartRecordingDialogContent
42
                 <StartRecordingDialogContent
43
+                    fileRecordingsServiceEnabled
44
+                        = { _fileRecordingsServiceEnabled }
35
                     integrationsEnabled = { _isDropboxEnabled }
45
                     integrationsEnabled = { _isDropboxEnabled }
36
                     isTokenValid = { isTokenValid }
46
                     isTokenValid = { isTokenValid }
37
                     isValidating = { isValidating }
47
                     isValidating = { isValidating }

+ 22
- 5
react/features/recording/components/Recording/styles.native.js Näytä tiedosto

6
 // the special case(s) of the recording feature bellow.
6
 // the special case(s) of the recording feature bellow.
7
 const _PADDING = BoxModel.padding * 1.5;
7
 const _PADDING = BoxModel.padding * 1.5;
8
 
8
 
9
+export const DROPBOX_LOGO
10
+    = require('../../../../../images/dropboxLogo_square.png');
11
+
12
+export const JITSI_LOGO
13
+    = require('../../../../../images/jitsiLogo_square.png');
14
+
9
 /**
15
 /**
10
  * The styles of the React {@code Components} of the feature recording.
16
  * The styles of the React {@code Components} of the feature recording.
11
  */
17
  */
28
         paddingBottom: _PADDING
34
         paddingBottom: _PADDING
29
     },
35
     },
30
 
36
 
31
-    startRecordingText: {
32
-        paddingBottom: _PADDING
37
+    recordingIcon: {
38
+        width: 24,
39
+        height: 24
33
     },
40
     },
34
 
41
 
35
-    switch: {
42
+    signButton: {
43
+        backgroundColor: ColorPalette.blue,
36
         color: ColorPalette.white,
44
         color: ColorPalette.white,
37
-        paddingRight: BoxModel.padding
45
+        fontSize: 16,
46
+        borderRadius: 5,
47
+        padding: BoxModel.padding * 0.5
48
+    },
49
+
50
+    switch: {
51
+        color: ColorPalette.white
38
     },
52
     },
39
 
53
 
40
     title: {
54
     title: {
55
+        flex: 1,
41
         fontSize: 16,
56
         fontSize: 16,
42
-        fontWeight: 'bold'
57
+        fontWeight: 'bold',
58
+        textAlign: 'left',
59
+        paddingLeft: BoxModel.padding
43
     },
60
     },
44
 
61
 
45
     text: {
62
     text: {

+ 4
- 0
react/features/recording/components/Recording/styles.web.js Näytä tiedosto

1
 // XXX CSS is used on Web, JavaScript styles are use only for mobile. Export an
1
 // XXX CSS is used on Web, JavaScript styles are use only for mobile. Export an
2
 // (empty) object so that styles[*] statements on Web don't trigger errors.
2
 // (empty) object so that styles[*] statements on Web don't trigger errors.
3
 export default {};
3
 export default {};
4
+
5
+export const DROPBOX_LOGO = 'images/dropboxLogo_square.png';
6
+
7
+export const JITSI_LOGO = 'images/jitsiLogo_square.png';

+ 14
- 4
react/features/recording/components/Recording/web/StartRecordingDialog.js Näytä tiedosto

25
      */
25
      */
26
     render() {
26
     render() {
27
         const { isTokenValid, isValidating, spaceLeft, userName } = this.state;
27
         const { isTokenValid, isValidating, spaceLeft, userName } = this.state;
28
-        const { _isDropboxEnabled } = this.props;
28
+        const { _fileRecordingsServiceEnabled, _isDropboxEnabled } = this.props;
29
+
30
+        // disable ok button id recording service is shown only, when
31
+        // validating dropbox token, if that is not enabled we either always
32
+        // show the ok button or if just dropbox is enabled ok is available
33
+        // when there is token
34
+        const isOkDisabled
35
+            = _fileRecordingsServiceEnabled ? isValidating
36
+                : _isDropboxEnabled ? !isTokenValid : false;
29
 
37
 
30
         return (
38
         return (
31
             <Dialog
39
             <Dialog
32
-                okDisabled = { !isTokenValid && _isDropboxEnabled }
33
-                okKey = 'dialog.confirm'
40
+                okDisabled = { isOkDisabled }
41
+                okKey = 'dialog.startRecording'
34
                 onSubmit = { this._onSubmit }
42
                 onSubmit = { this._onSubmit }
35
-                titleKey = 'dialog.recording'
43
+                titleKey = 'dialog.startRecording'
36
                 width = 'small'>
44
                 width = 'small'>
37
                 <StartRecordingDialogContent
45
                 <StartRecordingDialogContent
46
+                    fileRecordingsServiceEnabled
47
+                        = { _fileRecordingsServiceEnabled }
38
                     integrationsEnabled = { _isDropboxEnabled }
48
                     integrationsEnabled = { _isDropboxEnabled }
39
                     isTokenValid = { isTokenValid }
49
                     isTokenValid = { isTokenValid }
40
                     isValidating = { isValidating }
50
                     isValidating = { isValidating }

+ 4
- 5
react/features/recording/constants.js Näytä tiedosto

19
 export const RECORDING_ON_SOUND_ID = 'RECORDING_ON_SOUND';
19
 export const RECORDING_ON_SOUND_ID = 'RECORDING_ON_SOUND';
20
 
20
 
21
 /**
21
 /**
22
- * Expected supported recording types. JIBRI is known to support live streaming
23
- * whereas JIRECON is for recording.
22
+ * Expected supported recording types.
24
  *
23
  *
25
- * @type {Object}
24
+ * @enum {string}
26
  */
25
  */
27
 export const RECORDING_TYPES = {
26
 export const RECORDING_TYPES = {
28
-    JIBRI: 'jibri',
29
-    JIRECON: 'jirecon'
27
+    JITSI_REC_SERVICE: 'recording-service',
28
+    DROPBOX: 'dropbox'
30
 };
29
 };
31
 
30
 
32
 /**
31
 /**

Loading…
Peruuta
Tallenna