From 920c179f560526372e88e92b7ed30f2ab26036e6 Mon Sep 17 00:00:00 2001 From: Leonard Kim Date: Mon, 17 Dec 2018 14:25:41 -0800 Subject: [PATCH] fix(live-streaming): show warning if stream key seems wrong Provide a client-side notice if the YouTube live stream key looks like it might be in the wrong format. Normally the stream key looks like 4 groups of 4 numbers and letters, each separated by a dash. The warning does not block submission in case YouTube changes their stream key format. --- css/_recording.scss | 12 ++- lang/main.json | 1 + .../LiveStream/AbstractStreamKeyForm.js | 81 ++++++++++++++++++- .../LiveStream/web/StreamKeyForm.js | 20 +++-- 4 files changed, 104 insertions(+), 10 deletions(-) diff --git a/css/_recording.scss b/css/_recording.scss index 3c7491267..d2e42f823 100644 --- a/css/_recording.scss +++ b/css/_recording.scss @@ -65,6 +65,8 @@ } .form-footer { + display: flex; + margin-top: 5px; text-align: right; } @@ -95,9 +97,15 @@ .stream-key-form { .helper-link { - display: inline-block; cursor: pointer; - margin-top: 5px; + display: inline-block; + flex-shrink: 0; + margin-left: auto; + } + + .validation-error { + color:#FFD740; + font-size: 12px; } } } diff --git a/lang/main.json b/lang/main.json index 194551d42..6a0f2049b 100644 --- a/lang/main.json +++ b/lang/main.json @@ -515,6 +515,7 @@ "expandedOn": "The meeting is currently being streamed to YouTube.", "expandedPending": "The live streaming is being started...", "failedToStart": "Live Streaming failed to start", + "invalidStreamKey": "Live stream key may be incorrect.", "off": "Live Streaming stopped", "on": "Live Streaming", "pending": "Starting Live Stream...", diff --git a/react/features/recording/components/LiveStream/AbstractStreamKeyForm.js b/react/features/recording/components/LiveStream/AbstractStreamKeyForm.js index 278a03c74..d2b5ccf58 100644 --- a/react/features/recording/components/LiveStream/AbstractStreamKeyForm.js +++ b/react/features/recording/components/LiveStream/AbstractStreamKeyForm.js @@ -1,5 +1,6 @@ // @flow +import debounce from 'lodash/debounce'; import { Component } from 'react'; declare var interfaceConfig: Object; @@ -33,14 +34,27 @@ export type Props = { value: string }; +/** + * The state of the component. + */ +type State = { + + /** + * Whether or not to show the warnings that the passed in value seems like + * an improperly formatted stream key. + */ + showValidationError: boolean +}; + /** * An abstract React Component for entering a key for starting a YouTube live * stream. * * @extends Component */ -export default class AbstractStreamKeyForm extends Component { +export default class AbstractStreamKeyForm extends Component { helpURL: string; + _debouncedUpdateValidationErrorVisibility: Function; /** * Constructor for the component. @@ -50,14 +64,45 @@ export default class AbstractStreamKeyForm extends Component { constructor(props: Props) { super(props); + this.state = { + showValidationError: Boolean(this.props.value) + && !this._validateStreamKey(this.props.value) + }; + this.helpURL = (typeof interfaceConfig !== 'undefined' && interfaceConfig.LIVE_STREAMING_HELP_LINK) || LIVE_STREAMING_HELP_LINK; + this._debouncedUpdateValidationErrorVisibility = debounce( + this._updateValidationErrorVisibility.bind(this), + 800, + { leading: false } + ); + // Bind event handlers so they are only bound once per instance. this._onInputChange = this._onInputChange.bind(this); } + /** + * Implements React Component's componentDidUpdate. + * + * @inheritdoc + */ + componentDidUpdate(prevProps: Props) { + if (this.props.value !== prevProps.value) { + this._debouncedUpdateValidationErrorVisibility(); + } + } + + /** + * Implements React Component's componentWillUnmount. + * + * @inheritdoc + */ + componentWillUnmount() { + this._debouncedUpdateValidationErrorVisibility.cancel(); + } + _onInputChange: Object => void /** @@ -75,4 +120,38 @@ export default class AbstractStreamKeyForm extends Component { this.props.onChange(value); } + + /** + * Checks if the stream key value seems like a valid stream key and sets the + * state for showing or hiding the notification about the stream key seeming + * invalid. + * + * @private + * @returns {boolean} + */ + _updateValidationErrorVisibility() { + const newShowValidationError = Boolean(this.props.value) + && !this._validateStreamKey(this.props.value); + + if (newShowValidationError !== this.state.showValidationError) { + this.setState({ + showValidationError: newShowValidationError + }); + } + } + + /** + * Checks if a passed in stream key appears to be in a valid format. + * + * @param {string} streamKey - The stream key to check for valid formatting. + * @returns {void} + * @returns {boolean} + */ + _validateStreamKey(streamKey = '') { + const trimmedKey = streamKey.trim(); + const fourGroupsDashSeparated = /^(?:[a-zA-Z0-9]{4}(?:-(?!$)|$)){4}/; + const match = fourGroupsDashSeparated.exec(trimmedKey); + + return Boolean(match); + } } diff --git a/react/features/recording/components/LiveStream/web/StreamKeyForm.js b/react/features/recording/components/LiveStream/web/StreamKeyForm.js index 334029d00..d8bb9c279 100644 --- a/react/features/recording/components/LiveStream/web/StreamKeyForm.js +++ b/react/features/recording/components/LiveStream/web/StreamKeyForm.js @@ -36,7 +36,7 @@ class StreamKeyForm extends AbstractStreamKeyForm { * @returns {ReactElement} */ render() { - const { value, t } = this.props; + const { t, value } = this.props; return (
@@ -52,16 +52,22 @@ class StreamKeyForm extends AbstractStreamKeyForm { shouldFitContainer = { true } type = 'text' value = { this.props.value } /> - { this.helpURL - ? - : null - } + : null + } +
); }