Browse Source

Merge pull request #3696 from virtuacoplenny/lenny/stream-key-validation

Add some live stream key validation
master
virtuacoplenny 6 years ago
parent
commit
699b13066e
No account linked to committer's email address

+ 10
- 2
css/_recording.scss View File

@@ -65,6 +65,8 @@
65 65
     }
66 66
 
67 67
     .form-footer {
68
+        display: flex;
69
+        margin-top: 5px;
68 70
         text-align: right;
69 71
     }
70 72
 
@@ -95,9 +97,15 @@
95 97
 
96 98
     .stream-key-form {
97 99
         .helper-link {
98
-            display: inline-block;
99 100
             cursor: pointer;
100
-            margin-top: 5px;
101
+            display: inline-block;
102
+            flex-shrink: 0;
103
+            margin-left: auto;
104
+        }
105
+
106
+        .validation-error {
107
+            color:#FFD740;
108
+            font-size: 12px;
101 109
         }
102 110
     }
103 111
 }

+ 1
- 0
lang/main.json View File

@@ -515,6 +515,7 @@
515 515
         "expandedOn": "The meeting is currently being streamed to YouTube.",
516 516
         "expandedPending": "The live streaming is being started...",
517 517
         "failedToStart": "Live Streaming failed to start",
518
+        "invalidStreamKey": "Live stream key may be incorrect.",
518 519
         "off": "Live Streaming stopped",
519 520
         "on": "Live Streaming",
520 521
         "pending": "Starting Live Stream...",

+ 2
- 1
react/features/recording/components/LiveStream/AbstractStartLiveStreamDialog.js View File

@@ -194,7 +194,8 @@ export default class AbstractStartLiveStreamDialog<P: Props>
194 194
      */
195 195
     _onSubmit() {
196 196
         const { broadcasts, selectedBoundStreamID } = this.state;
197
-        const key = this.state.streamKey || this.props._streamKey;
197
+        const key
198
+            = (this.state.streamKey || this.props._streamKey || '').trim();
198 199
 
199 200
         if (!key) {
200 201
             return false;

+ 80
- 1
react/features/recording/components/LiveStream/AbstractStreamKeyForm.js View File

@@ -1,5 +1,6 @@
1 1
 // @flow
2 2
 
3
+import debounce from 'lodash/debounce';
3 4
 import { Component } from 'react';
4 5
 
5 6
 declare var interfaceConfig: Object;
@@ -33,14 +34,27 @@ export type Props = {
33 34
     value: string
34 35
 };
35 36
 
37
+/**
38
+ * The state of the component.
39
+ */
40
+type State = {
41
+
42
+    /**
43
+     * Whether or not to show the warnings that the passed in value seems like
44
+     * an improperly formatted stream key.
45
+     */
46
+    showValidationError: boolean
47
+};
48
+
36 49
 /**
37 50
  * An abstract React Component for entering a key for starting a YouTube live
38 51
  * stream.
39 52
  *
40 53
  * @extends Component
41 54
  */
42
-export default class AbstractStreamKeyForm extends Component<Props> {
55
+export default class AbstractStreamKeyForm extends Component<Props, State> {
43 56
     helpURL: string;
57
+    _debouncedUpdateValidationErrorVisibility: Function;
44 58
 
45 59
     /**
46 60
      * Constructor for the component.
@@ -50,14 +64,45 @@ export default class AbstractStreamKeyForm extends Component<Props> {
50 64
     constructor(props: Props) {
51 65
         super(props);
52 66
 
67
+        this.state = {
68
+            showValidationError: Boolean(this.props.value)
69
+                && !this._validateStreamKey(this.props.value)
70
+        };
71
+
53 72
         this.helpURL = (typeof interfaceConfig !== 'undefined'
54 73
             && interfaceConfig.LIVE_STREAMING_HELP_LINK)
55 74
             || LIVE_STREAMING_HELP_LINK;
56 75
 
76
+        this._debouncedUpdateValidationErrorVisibility = debounce(
77
+            this._updateValidationErrorVisibility.bind(this),
78
+            800,
79
+            { leading: false }
80
+        );
81
+
57 82
         // Bind event handlers so they are only bound once per instance.
58 83
         this._onInputChange = this._onInputChange.bind(this);
59 84
     }
60 85
 
86
+    /**
87
+     * Implements React Component's componentDidUpdate.
88
+     *
89
+     * @inheritdoc
90
+     */
91
+    componentDidUpdate(prevProps: Props) {
92
+        if (this.props.value !== prevProps.value) {
93
+            this._debouncedUpdateValidationErrorVisibility();
94
+        }
95
+    }
96
+
97
+    /**
98
+     * Implements React Component's componentWillUnmount.
99
+     *
100
+     * @inheritdoc
101
+     */
102
+    componentWillUnmount() {
103
+        this._debouncedUpdateValidationErrorVisibility.cancel();
104
+    }
105
+
61 106
     _onInputChange: Object => void
62 107
 
63 108
     /**
@@ -75,4 +120,38 @@ export default class AbstractStreamKeyForm extends Component<Props> {
75 120
 
76 121
         this.props.onChange(value);
77 122
     }
123
+
124
+    /**
125
+     * Checks if the stream key value seems like a valid stream key and sets the
126
+     * state for showing or hiding the notification about the stream key seeming
127
+     * invalid.
128
+     *
129
+     * @private
130
+     * @returns {boolean}
131
+     */
132
+    _updateValidationErrorVisibility() {
133
+        const newShowValidationError = Boolean(this.props.value)
134
+            && !this._validateStreamKey(this.props.value);
135
+
136
+        if (newShowValidationError !== this.state.showValidationError) {
137
+            this.setState({
138
+                showValidationError: newShowValidationError
139
+            });
140
+        }
141
+    }
142
+
143
+    /**
144
+     * Checks if a passed in stream key appears to be in a valid format.
145
+     *
146
+     * @param {string} streamKey - The stream key to check for valid formatting.
147
+     * @returns {void}
148
+     * @returns {boolean}
149
+     */
150
+    _validateStreamKey(streamKey = '') {
151
+        const trimmedKey = streamKey.trim();
152
+        const fourGroupsDashSeparated = /^(?:[a-zA-Z0-9]{4}(?:-(?!$)|$)){4}/;
153
+        const match = fourGroupsDashSeparated.exec(trimmedKey);
154
+
155
+        return Boolean(match);
156
+    }
78 157
 }

+ 22
- 9
react/features/recording/components/LiveStream/native/StreamKeyForm.js View File

@@ -52,15 +52,28 @@ class StreamKeyForm extends AbstractStreamKeyForm {
52 52
                     placeholderTextColor = { PLACEHOLDER_COLOR }
53 53
                     style = { styles.streamKeyInput }
54 54
                     value = { this.props.value } />
55
-                <TouchableOpacity
56
-                    onPress = { this._onOpenHelp }
57
-                    style = { styles.streamKeyHelp } >
58
-                    <Text style = { styles.text }>
59
-                        {
60
-                            t('liveStreaming.streamIdHelp')
61
-                        }
62
-                    </Text>
63
-                </TouchableOpacity>
55
+                <View style = { styles.formFooter }>
56
+                    {
57
+                        this.state.showValidationError
58
+                            ? <View style = { styles.formFooterItem }>
59
+                                <Text style = { styles.warningText }>
60
+                                    { t('liveStreaming.invalidStreamKey') }
61
+                                </Text>
62
+                            </View>
63
+                            : null
64
+                    }
65
+                    <View style = { styles.formFooterItem }>
66
+                        <TouchableOpacity
67
+                            onPress = { this._onOpenHelp }
68
+                            style = { styles.streamKeyHelp } >
69
+                            <Text style = { styles.text }>
70
+                                {
71
+                                    t('liveStreaming.streamIdHelp')
72
+                                }
73
+                            </Text>
74
+                        </TouchableOpacity>
75
+                    </View>
76
+                </View>
64 77
             </View>
65 78
         );
66 79
     }

+ 21
- 1
react/features/recording/components/LiveStream/native/styles.js View File

@@ -48,6 +48,20 @@ export default createStyleSheet({
48 48
         padding: BoxModel.padding
49 49
     },
50 50
 
51
+    /**
52
+     * Wrapper for the last element in the form.
53
+     */
54
+    formFooter: {
55
+        flexDirection: 'row'
56
+    },
57
+
58
+    /**
59
+     * Wrapper for individual children in the last element of the form.
60
+     */
61
+    formFooterItem: {
62
+        flex: 1
63
+    },
64
+
51 65
     /**
52 66
      * Explaining text on the top of the sign in form.
53 67
      */
@@ -133,6 +147,12 @@ export default createStyleSheet({
133 147
 
134 148
     text: {
135 149
         color: ColorPalette.white
136
-    }
150
+    },
137 151
 
152
+    /**
153
+     * A different colored text to indicate information needing attention.
154
+     */
155
+    warningText: {
156
+        color: ColorPalette.Y200
157
+    }
138 158
 });

+ 14
- 7
react/features/recording/components/LiveStream/web/StreamKeyForm.js View File

@@ -36,7 +36,7 @@ class StreamKeyForm extends AbstractStreamKeyForm {
36 36
      * @returns {ReactElement}
37 37
      */
38 38
     render() {
39
-        const { value, t } = this.props;
39
+        const { t, value } = this.props;
40 40
 
41 41
         return (
42 42
             <div className = 'stream-key-form'>
@@ -52,16 +52,23 @@ class StreamKeyForm extends AbstractStreamKeyForm {
52 52
                     shouldFitContainer = { true }
53 53
                     type = 'text'
54 54
                     value = { this.props.value } />
55
-                { this.helpURL
56
-                    ? <div className = 'form-footer'>
57
-                        <a
55
+                <div className = 'form-footer'>
56
+                    {
57
+                        this.state.showValidationError
58
+                            ? <span className = 'validation-error'>
59
+                                { t('liveStreaming.invalidStreamKey') }
60
+                            </span>
61
+                            : null
62
+                    }
63
+                    { this.helpURL
64
+                        ? <a
58 65
                             className = 'helper-link'
59 66
                             onClick = { this._onOpenHelp }>
60 67
                             { t('liveStreaming.streamIdHelp') }
61 68
                         </a>
62
-                    </div>
63
-                    : null
64
-                }
69
+                        : null
70
+                    }
71
+                </div>
65 72
             </div>
66 73
         );
67 74
     }

Loading…
Cancel
Save