Compare commits

...

30 Commits

Author SHA1 Message Date
virtuacoplenny
957606b3f8 Merge pull request #3630 from virtuacoplenny/lenny/youtube-1-on-1
fix(filmstrip): show thumbnails in 1-on-1 with a fake participant
2018-11-27 14:36:43 -08:00
Leonard Kim
769a2c7c94 fix(filmstrip): show thumbnails in 1-on-1 with a fake participant
Filmstrip remote thumbnails display under certain conditions, as
defined in filmstrip/functions.web.js. Previously the raw
participant count was used, which included fake participants.
Using the selector getParticipantCount excludes fake participants,
causing YouTube thumbnails to remain hidden in a 1-on-1 call.
2018-11-27 12:31:27 -08:00
virtuacoplenny
f349357d3c Merge pull request #3584 from virtuacoplenny/lenny/update-lifecycles-1
Remove some usages of deprecated lifecycle methods
2018-11-27 09:02:05 -08:00
virtuacoplenny
9c2f816c29 Merge pull request #3619 from mmoanis/dialog-with-tabs
Abstract the DialogWithTabs title so it can be reused with other comp…
2018-11-24 10:31:58 -08:00
mmoanis
b844a9f06b Abstract the DialogWithTabs title so it can be reused with other components 2018-11-24 12:50:09 +01:00
Leonard Kim
d4e18e78fa ref(recording-label): derive when the label state is no longer stale 2018-11-21 08:08:45 -08:00
Leonard Kim
f13cfe70f3 ref(sidebar): derive showOverlay state
- Derive the showOverlay state. When the sidebar should be hidden,
  the internal showOverlay state should remain true until the
  animation hides it. When the sidebar should show, the showOverlay
  state should become true immediately.
- Use PureComponent to prevent additional animation triggers
  instead of explicitly checking changes to the "show" prop.
2018-11-21 08:08:45 -08:00
Leonard Kim
5cb4bec633 ref(circular-label): animate after dom updates
Based on react-native docs, looks like animations should be
started after mount. Updating animation states I'm not certain
on so I moved it to componentDidUpdate and tested with the
live streaming label to ensure the component still animated fine.
2018-11-21 08:08:45 -08:00
Leonard Kim
4409bbabb7 ref(blank-page): destroy local track after mount
To kill componentWillMount, call destroyLocalTrack after mount.
Navigation to the blank page was synthetically forced and no
UI issues were noticed, possibly because destroyLocalTrack may
already be async so destruction may already have been occurring
after mount.
2018-11-21 08:08:45 -08:00
Leonard Kim
d6216f21d5 ref(live-streaming): remove picker state to remove componentWillReceiveProps 2018-11-21 08:08:45 -08:00
Leonard Kim
3a32f7f3f0 ref(audio-picker): fetch audio devices after mount
Per react migration docs, initially fetching external data is
recommended to be done in componentDidMount.
2018-11-21 08:08:45 -08:00
Leonard Kim
d5fb2c2717 ref(sdk): update comments to exclude mention of componentWillReceiveProps 2018-11-21 08:08:45 -08:00
Leonard Kim
c4f1588bb0 ref(dialog): set mounted flag after mount 2018-11-21 08:08:45 -08:00
Leonard Kim
609f3887f2 ref(welcome-page): native creates/destroys camera after mount 2018-11-21 08:08:45 -08:00
Leonard Kim
77f8f85b96 ref(device-selection): update preview tracks on component update 2018-11-21 08:08:45 -08:00
Leonard Kim
14adc0b887 ref(always-on-top): trigger toolbar hide timeout after update 2018-11-21 08:08:45 -08:00
Leonard Kim
c288d0e18c ref(deep-linking): set initial state in constructor 2018-11-21 08:08:45 -08:00
Leonard Kim
eaafc21133 ref(desktop-picker): derive desired types when props change 2018-11-21 08:08:45 -08:00
Leonard Kim
72c1fa38be ref(modal): simplify functional footer passing to remove componentWillUpdate 2018-11-21 08:08:45 -08:00
Leonard Kim
45068f68db ref(welcome-page): use getDerivedStateFromProps, set mounted after actual mount 2018-11-21 08:08:45 -08:00
Leonard Kim
e0cbb838be ref(info): derive when to clear the entered password state 2018-11-21 08:08:45 -08:00
Leonard Kim
c28c70fb2f ref(device-selection): change audio preview listener on component update 2018-11-21 08:08:45 -08:00
Leonard Kim
280178f5d1 ref(info-dialog): derive when to autoshow or autohide 2018-11-21 08:08:45 -08:00
Leonard Kim
e9b2518f8a ref(info): use getDerivedStateFromProps to update state 2018-11-21 08:08:45 -08:00
Leonard Kim
1e3e71c2ff ref(speaker-stats): begin polling for stats after mount 2018-11-21 08:08:45 -08:00
Leonard Kim
007d60eb6c ref(toolbox): getter for the recording/streaming disabled tooltip 2018-11-21 08:08:45 -08:00
Leonard Kim
85f487cca5 ref(large-video): use componentDidUpdate to change background image 2018-11-21 08:08:44 -08:00
Leonard Kim
b24e7ec5f0 ref(labels): use getDerivedStateFromProps to get display state 2018-11-21 08:08:44 -08:00
Leonard Kim
a045353e6e ref(tooltbox): use componentDidUpdate to trigger more changes 2018-11-21 08:08:44 -08:00
Aaron van Meerten
420c466f80 Merge pull request #3612 from jitsi/node_10_build_support
updated node-sass version for node 10
2018-11-20 17:31:12 -06:00
34 changed files with 307 additions and 377 deletions

View File

@@ -262,7 +262,7 @@ public class JitsiMeetView
// the respective conference again if the first invocation was followed
// by leaving the conference. However, React and, respectively,
// appProperties/initialProperties are declarative expressions i.e. one
// and the same URL will not trigger componentWillReceiveProps in the
// and the same URL will not trigger an automatic re-render in the
// JavaScript source code. The workaround implemented bellow introduces
// imperativeness in React Component props by defining a unique value
// per loadURLObject: invocation.

View File

@@ -268,7 +268,7 @@ static NSMapTable<NSString *, JitsiMeetView *> *views;
// conference again if the first invocation was followed by leaving the
// conference. However, React and, respectively,
// appProperties/initialProperties are declarative expressions i.e. one and
// the same URL will not trigger componentWillReceiveProps in the JavaScript
// the same URL will not trigger an automatic re-render in the JavaScript
// source code. The workaround implemented bellow introduces imperativeness
// in React Component props by defining a unique value per loadURLObject:
// invocation.

View File

@@ -205,6 +205,18 @@ export default class AlwaysOnTop extends Component<*, State> {
this._hideToolbarAfterTimeout();
}
/**
* Sets a timeout to hide the toolbar when the toolbar is shown.
*
* @inheritdoc
* @returns {void}
*/
componentDidUpdate(prevProps: *, prevState: State) {
if (!prevState.visible && this.state.visible) {
this._hideToolbarAfterTimeout();
}
}
/**
* Removes all listeners.
*
@@ -223,18 +235,6 @@ export default class AlwaysOnTop extends Component<*, State> {
window.removeEventListener('mousemove', this._mouseMove);
}
/**
* Sets a timeout to hide the toolbar when the toolbar is shown.
*
* @inheritdoc
* @returns {void}
*/
componentWillUpdate(nextProps: *, nextState: State) {
if (!this.state.visible && nextState.visible) {
this._hideToolbarAfterTimeout();
}
}
/**
* Implements React's {@link Component#render()}.
*

View File

@@ -49,12 +49,12 @@ export default class AbstractDialog<P : Props, S : State>
}
/**
* Implements React's {@link Component#componentWillMount()}. Invoked
* Implements React's {@link Component#componentDidMount()}. Invoked
* immediately before mounting occurs.
*
* @inheritdoc
*/
componentWillMount() {
componentDidMount() {
this._mounted = true;
}

View File

@@ -18,6 +18,11 @@ export type Props = {
*/
closeDialog: Function,
/**
* Css class name that will be added to the dialog.
*/
cssClassName: string,
/**
* Which settings tab should be initially displayed. If not defined then
* the first tab will be displayed.
@@ -44,7 +49,12 @@ export type Props = {
/**
* Information about the tabs that will be rendered.
*/
tabs: Array<Object>
tabs: Array<Object>,
/**
* Key to use for showing a title.
*/
titleKey: string
};
@@ -102,8 +112,8 @@ class DialogWithTabs extends Component<Props, State> {
= { this.props.disableBlanketClickDismiss }
onCancel = { onCancel }
onSubmit = { this._onSubmit }
titleKey = 'settings.title'>
<div className = 'settings-dialog'>
titleKey = { this.props.titleKey } >
<div className = { this.props.cssClassName } >
{ this._renderTabs() }
</div>
</StatelessDialog>

View File

@@ -102,26 +102,8 @@ class StatelessDialog extends Component<Props> {
this._onDialogDismissed = this._onDialogDismissed.bind(this);
this._onKeyDown = this._onKeyDown.bind(this);
this._onSubmit = this._onSubmit.bind(this);
this._renderFooter = this._renderFooter.bind(this);
this._setDialogElement = this._setDialogElement.bind(this);
this._Footer = this._createFooterConstructor(props);
}
/**
* React Component method that executes before the component is updated.
*
* @inheritdoc
* @param {Object} nextProps - The next properties, before the update.
* @returns {void}
*/
componentWillUpdate(nextProps) {
// If button states have changed, update the Footer constructor function
// so buttons of the proper state are rendered.
if (nextProps.okDisabled !== this.props.okDisabled
|| nextProps.cancelDisabled !== this.props.cancelDisabled
|| nextProps.submitDisabled !== this.props.submitDisabled) {
this._Footer = this._createFooterConstructor(nextProps);
}
}
/**
@@ -142,7 +124,7 @@ class StatelessDialog extends Component<Props> {
return (
<Modal
autoFocus = { true }
footer = { this._Footer }
footer = { this._renderFooter }
heading = { titleString || t(titleKey) }
i18n = { this.props.i18n }
onClose = { this._onDialogDismissed }
@@ -174,41 +156,38 @@ class StatelessDialog extends Component<Props> {
);
}
_onCancel: () => Function;
_renderFooter: () => React$Node;
/**
* Returns a functional component to be used for the modal footer.
* Returns a ReactElement to display buttons for closing the modal.
*
* @param {Object} options - The configuration for how the buttons in the
* footer should display. Essentially {@code StatelessDialog} props should
* be passed in.
* @param {Object} propsFromModalFooter - The props passed in from the
* {@link ModalFooter} component.
* @private
* @returns {ReactElement}
*/
_createFooterConstructor(options) {
_renderFooter(propsFromModalFooter) {
// Filter out falsy (null) values because {@code ButtonGroup} will error
// if passed in anything but buttons with valid type props.
const buttons = [
this._renderOKButton(options),
this._renderCancelButton(options)
this._renderOKButton(),
this._renderCancelButton()
].filter(Boolean);
return function Footer(modalFooterProps) {
return (
<ModalFooter showKeyline = { modalFooterProps.showKeyline } >
{
return (
<ModalFooter showKeyline = { propsFromModalFooter.showKeyline } >
{
/**
* Atlaskit has this empty span (JustifySim) so...
*/
}
<span />
<ButtonGroup>
{ buttons }
</ButtonGroup>
</ModalFooter>
);
};
/**
* Atlaskit has this empty span (JustifySim) so...
*/
}
<span />
<ButtonGroup>
{ buttons }
</ButtonGroup>
</ModalFooter>
);
}
_onCancel: () => void;
@@ -257,21 +236,14 @@ class StatelessDialog extends Component<Props> {
/**
* Renders Cancel button.
*
* @param {Object} options - The configuration for the Cancel button.
* @param {boolean} options.cancelDisabled - True if the cancel button
* should not be rendered.
* @param {string} options.cancelTitleKey - The translation key to use as
* text on the button.
* @param {boolean} options.isModal - True if the cancel button should not
* be rendered.
* @private
* @returns {ReactElement|null} The Cancel button if enabled and dialog is
* not modal.
*/
_renderCancelButton(options = {}) {
if (options.cancelDisabled
|| options.isModal
|| options.hideCancelButton) {
_renderCancelButton() {
if (this.props.cancelDisabled
|| this.props.isModal
|| this.props.hideCancelButton) {
return null;
}
@@ -286,7 +258,7 @@ class StatelessDialog extends Component<Props> {
key = 'cancel'
onClick = { this._onCancel }
type = 'button'>
{ t(options.cancelTitleKey || 'dialog.Cancel') }
{ t(this.props.cancelTitleKey || 'dialog.Cancel') }
</Button>
);
}
@@ -294,18 +266,11 @@ class StatelessDialog extends Component<Props> {
/**
* Renders OK button.
*
* @param {Object} options - The configuration for the OK button.
* @param {boolean} options.okDisabled - True if the button should display
* as disabled and clicking should have no effect.
* @param {string} options.okTitleKey - The translation key to use as text
* on the button.
* @param {boolean} options.submitDisabled - True if the button should not
* be rendered.
* @private
* @returns {ReactElement|null} The OK button if enabled.
*/
_renderOKButton(options = {}) {
if (options.submitDisabled) {
_renderOKButton() {
if (this.props.submitDisabled) {
return null;
}
@@ -318,11 +283,11 @@ class StatelessDialog extends Component<Props> {
appearance = 'primary'
form = 'modal-dialog-form'
id = { OK_BUTTON_ID }
isDisabled = { options.okDisabled }
isDisabled = { this.props.okDisabled }
key = 'submit'
onClick = { this._onSubmit }
type = 'button'>
{ t(options.okTitleKey || 'dialog.Ok') }
{ t(this.props.okTitleKey || 'dialog.Ok') }
</Button>
);
}

View File

@@ -64,17 +64,24 @@ export default class CircularLabel extends AbstractCircularLabel<Props, State> {
this.state = {
pulseAnimation: new Animated.Value(0)
};
this._maybeToggleAnimation({}, props);
}
/**
* Implements {@code Component#componentWillReceiveProps}.
* Implements {@code Component#componentDidMount}.
*
* @inheritdoc
*/
componentWillReceiveProps(newProps: Props) {
this._maybeToggleAnimation(this.props, newProps);
componentDidMount() {
this._maybeToggleAnimation({}, this.props);
}
/**
* Implements {@code Component#componentDidUpdate}.
*
* @inheritdoc
*/
componentDidUpdate(prevProps: Props) {
this._maybeToggleAnimation(prevProps, this.props);
}
/**

View File

@@ -125,6 +125,20 @@ export function getParticipantCount(stateful: Object | Function) {
return getParticipants(stateful).length;
}
/**
* Returns a count of the known participants in the passed in redux state,
* including fake participants.
*
* @param {(Function|Object|Participant[])} stateful - The redux state
* features/base/participants, the (whole) redux state, or redux's
* {@code getState} function to be used to retrieve the state
* features/base/participants.
* @returns {number}
*/
export function getParticipantCountWithFake(stateful: Object | Function) {
return _getAllParticipants(stateful).length;
}
/**
* Returns participant's display name.
*

View File

@@ -1,6 +1,6 @@
// @flow
import React, { Component, type Node } from 'react';
import React, { PureComponent, type Node } from 'react';
import { Animated, TouchableWithoutFeedback, View } from 'react-native';
import styles, { SIDEBAR_WIDTH } from './styles';
@@ -46,7 +46,18 @@ type State = {
/**
* A generic animated side bar to be used for left-side, hamburger-style menus.
*/
export default class SideBar extends Component<Props, State> {
export default class SideBar extends PureComponent<Props, State> {
/**
* Implements React's {@link Component#getDerivedStateFromProps()}.
*
* @inheritdoc
*/
static getDerivedStateFromProps(props: Props, prevState: State) {
return {
showOverlay: props.show || prevState.showOverlay
};
}
/**
* Initializes a new {@code SideBar} instance.
*
@@ -74,12 +85,12 @@ export default class SideBar extends Component<Props, State> {
}
/**
* Implements React's {@link Component#componentWillReceiveProps()}.
* Implements React's {@link Component#componentDidUpdate()}.
*
* @inheritdoc
*/
componentWillReceiveProps({ show }: Props) {
(show === this.props.show) || this._setShow(show);
componentDidUpdate() {
this._setShow(this.props.show);
}
/**
@@ -148,8 +159,6 @@ export default class SideBar extends Component<Props, State> {
* @returns {void}
*/
_setShow(show) {
show && this.setState({ showOverlay: true });
Animated
.timing(
/* value */ this.state.sliderAnimation,

View File

@@ -206,6 +206,16 @@ export default class AbstractButton<P: Props, S: *> extends Component<P, S> {
return buttonStyles;
}
/**
* Get the tooltip to display when hovering over the button.
*
* @private
* @returns {string}
*/
_getTooltip() {
return this.tooltip || '';
}
/**
* Helper function to be implemented by subclasses, which must return a
* boolean value indicating if this button is disabled or not.
@@ -258,7 +268,7 @@ export default class AbstractButton<P: Props, S: *> extends Component<P, S> {
iconName: this._getIconName(),
label: this._getLabel(),
styles: this._getStyles(),
tooltip: this.tooltip
tooltip: this._getTooltip()
};
return (

View File

@@ -81,23 +81,15 @@ class DeepLinkingMobilePage extends Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = {
joinURL: generateDeepLinkingURL()
};
// Bind event handlers so they are only bound once per instance.
this._onDownloadApp = this._onDownloadApp.bind(this);
this._onOpenApp = this._onOpenApp.bind(this);
}
/**
* Initializes the text and URL of the `Start a conference` / `Join the
* conversation` button which takes the user to the mobile app.
*
* @inheritdoc
*/
componentWillMount() {
this.setState({
joinURL: generateDeepLinkingURL()
});
}
/**
* Implements the Component's componentDidMount method.
*

View File

@@ -1,7 +1,7 @@
// @flow
import Tabs from '@atlaskit/tabs';
import React, { Component } from 'react';
import React, { PureComponent } from 'react';
import { connect } from 'react-redux';
import { Dialog, hideDialog } from '../../base/dialog';
@@ -94,12 +94,36 @@ type State = {
types: Array<string>
};
/**
* React component for DesktopPicker.
*
* @extends Component
*/
class DesktopPicker extends Component<Props, State> {
class DesktopPicker extends PureComponent<Props, State> {
/**
* Implements React's {@link Component#getDerivedStateFromProps()}.
*
* @inheritdoc
*/
static getDerivedStateFromProps(props) {
return {
types: DesktopPicker._getValidTypes(props.desktopSharingSources)
};
}
/**
* Extracts only the valid types from the passed {@code types}.
*
* @param {Array<string>} types - The types to filter.
* @private
* @returns {Array<string>} The filtered types.
*/
static _getValidTypes(types = []) {
return types.filter(
type => VALID_TYPES.includes(type));
}
_poller = null;
state = {
@@ -133,7 +157,7 @@ class DesktopPicker extends Component<Props, State> {
this._updateSources = this._updateSources.bind(this);
this.state.types
= this._getValidTypes(this.props.desktopSharingSources);
= DesktopPicker._getValidTypes(this.props.desktopSharingSources);
}
/**
@@ -146,31 +170,6 @@ class DesktopPicker extends Component<Props, State> {
this._startPolling();
}
/**
* Notifies this mounted React Component that it will receive new props.
* Sets a default selected source if one is not already set.
*
* @inheritdoc
* @param {Object} nextProps - The read-only React Component props that this
* instance will receive.
* @returns {void}
*/
componentWillReceiveProps(nextProps: Props) {
const { desktopSharingSources } = nextProps;
/**
* Do only reference check in order to not calculate the types on every
* update. This is enough for our use case and we don't need value
* checking because if the value is the same we won't change the
* reference for the desktopSharingSources array.
*/
if (desktopSharingSources !== this.props.desktopSharingSources) {
this.setState({
types: this._getValidTypes(desktopSharingSources)
});
}
}
/**
* Clean up component and DesktopCapturerSource store state.
*
@@ -241,17 +240,6 @@ class DesktopPicker extends Component<Props, State> {
return selectedSource;
}
/**
* Extracts only the valid types from the passed {@code types}.
*
* @param {Array<string>} types - The types to filter.
* @returns {Array<string>} The filtered types.
*/
_getValidTypes(types = []) {
return types.filter(
type => VALID_TYPES.includes(type));
}
_onCloseModal: (?string, string) => void;
/**

View File

@@ -63,9 +63,9 @@ class AudioInputPreview extends Component<Props, State> {
* @inheritdoc
* @returns {void}
*/
componentWillReceiveProps(nextProps: Props) {
if (nextProps.track !== this.props.track) {
this._listenForAudioUpdates(nextProps.track);
componentDidUpdate(prevProps: Props) {
if (prevProps.track !== this.props.track) {
this._listenForAudioUpdates(this.props.track);
this._updateAudioLevel(0);
}
}

View File

@@ -151,7 +151,8 @@ class DeviceSelection extends AbstractDialogTab<Props, State> {
}
/**
* Checks if audio / video permissions were granted.
* Checks if audio / video permissions were granted. Updates audio input and
* video input previews.
*
* @param {Object} prevProps - Previous props this component received.
* @param {Object} prevState - Previous state this component had.
@@ -174,25 +175,15 @@ class DeviceSelection extends AbstractDialogTab<Props, State> {
});
});
}
}
/**
* Updates audio input and video input previews.
*
* @inheritdoc
* @param {Object} nextProps - The read-only props which this Component will
* receive.
* @returns {void}
*/
componentWillReceiveProps(nextProps: Object) {
const { selectedAudioInputId, selectedVideoInputId } = this.props;
if (selectedAudioInputId !== nextProps.selectedAudioInputId) {
this._createAudioInputTrack(nextProps.selectedAudioInputId);
if (prevProps.selectedAudioInputId
!== this.props.selectedAudioInputId) {
this._createAudioInputTrack(this.props.selectedAudioInputId);
}
if (selectedVideoInputId !== nextProps.selectedVideoInputId) {
this._createVideoInputTrack(nextProps.selectedVideoInputId);
if (prevProps.selectedVideoInputId
!== this.props.selectedVideoInputId) {
this._createVideoInputTrack(this.props.selectedVideoInputId);
}
}

View File

@@ -1,7 +1,7 @@
// @flow
import {
getParticipantCount,
getParticipantCountWithFake,
getPinnedParticipant
} from '../base/participants';
import { toState } from '../base/redux';
@@ -36,7 +36,10 @@ export function shouldRemoteVideosBeVisible(state: Object) {
return false;
}
const participantCount = getParticipantCount(state);
// Include fake participants to derive how many thumbnails are dispalyed,
// as it is assumed all participants, including fake, will be displayed
// in the filmstrip.
const participantCount = getParticipantCountWithFake(state);
let pinnedParticipant;
return Boolean(

View File

@@ -70,6 +70,12 @@ type Props = {
*/
type State = {
/**
* Cache the conference connection state to derive when transitioning from
* not joined to join, in order to auto-show the InfoDialog.
*/
hasConnectedToConference: boolean,
/**
* Whether or not {@code InfoDialog} should be visible.
*/
@@ -83,6 +89,23 @@ type State = {
* @extends Component
*/
class InfoDialogButton extends Component<Props, State> {
/**
* Implements React's {@link Component#getDerivedStateFromProps()}.
*
* @inheritdoc
*/
static getDerivedStateFromProps(props, state) {
return {
hasConnectedToConference: props._isConferenceJoined,
showDialog: (props._toolboxVisible && state.showDialog)
|| (!state.hasConnectedToConference
&& props._isConferenceJoined
&& props._participantCount < 2
&& props._toolboxVisible
&& !props._disableAutoShow)
};
}
/**
* Initializes new {@code InfoDialogButton} instance.
*
@@ -92,6 +115,7 @@ class InfoDialogButton extends Component<Props, State> {
super(props);
this.state = {
hasConnectedToConference: props._isConferenceJoined,
showDialog: false
};
@@ -111,22 +135,6 @@ class InfoDialogButton extends Component<Props, State> {
}
}
/**
* Update the visibility of the {@code InfoDialog}.
*
* @inheritdoc
*/
componentWillReceiveProps(nextProps) {
// Ensure the dialog is closed when the toolbox becomes hidden.
if (this.state.showDialog && !nextProps._toolboxVisible) {
this._onDialogClose();
return;
}
this._maybeAutoShowDialog(nextProps);
}
/**
* Implements React's {@link Component#render()}.
*
@@ -159,24 +167,6 @@ class InfoDialogButton extends Component<Props, State> {
);
}
/**
* Invoked to trigger display of the {@code InfoDialog} if certain
* conditions are met.
*
* @param {Object} nextProps - The future properties of this component.
* @private
* @returns {void}
*/
_maybeAutoShowDialog(nextProps) {
if (!this.props._isConferenceJoined
&& nextProps._isConferenceJoined
&& nextProps._participantCount < 2
&& nextProps._toolboxVisible
&& !nextProps._disableAutoShow) {
this.setState({ showDialog: true });
}
}
_onDialogClose: () => void;
/**

View File

@@ -112,6 +112,28 @@ type State = {
class InfoDialog extends Component<Props, State> {
_copyElement: ?Object;
/**
* Implements React's {@link Component#getDerivedStateFromProps()}.
*
* @inheritdoc
*/
static getDerivedStateFromProps(props, state) {
let phoneNumber = state.phoneNumber;
if (!state.phoneNumber && props.dialIn.numbers) {
const { defaultCountry, numbers } = props.dialIn;
phoneNumber = _getDefaultPhoneNumber(numbers, defaultCountry);
}
return {
// Exit edit mode when a password is set locally or remotely.
passwordEditEnabled: state.passwordEditEnabled && props._password
? false : state.passwordEditEnabled,
phoneNumber
};
}
/**
* {@code InfoDialog} component's local state.
*
@@ -162,28 +184,6 @@ class InfoDialog extends Component<Props, State> {
this._setCopyElement = this._setCopyElement.bind(this);
}
/**
* Implements React's {@link Component#componentWillReceiveProps()}. Invoked
* before this mounted component receives new props.
*
* @inheritdoc
* @param {Props} nextProps - New props component will receive.
*/
componentWillReceiveProps(nextProps) {
if (!this.props._password && nextProps._password) {
this.setState({ passwordEditEnabled: false });
}
if (!this.state.phoneNumber && nextProps.dialIn.numbers) {
const { defaultCountry, numbers } = nextProps.dialIn;
this.setState({
phoneNumber:
_getDefaultPhoneNumber(numbers, defaultCountry)
});
}
}
/**
* Implements React's {@link Component#render()}.
*

View File

@@ -55,6 +55,17 @@ type State = {
* @extends Component
*/
class PasswordForm extends Component<Props, State> {
/**
* Implements React's {@link Component#getDerivedStateFromProps()}.
*
* @inheritdoc
*/
static getDerivedStateFromProps(props, state) {
return {
enteredPassword: props.editEnabled ? state.enteredPassword : ''
};
}
state = {
enteredPassword: ''
};
@@ -74,19 +85,6 @@ class PasswordForm extends Component<Props, State> {
this._onPasswordSubmit = this._onPasswordSubmit.bind(this);
}
/**
* Implements React's {@link Component#componentWillReceiveProps()}. Invoked
* before this mounted component receives new props.
*
* @inheritdoc
* @param {Props} nextProps - New props component will receive.
*/
componentWillReceiveProps(nextProps: Props) {
if (this.props.editEnabled && !nextProps.editEnabled) {
this.setState({ enteredPassword: '' });
}
}
/**
* Implements React's {@link Component#render()}.
*
@@ -182,5 +180,4 @@ class PasswordForm extends Component<Props, State> {
}
}
export default translate(PasswordForm);

View File

@@ -32,6 +32,19 @@ type State = {
* @extends Component
*/
class Labels extends AbstractLabels<Props, State> {
/**
* Updates the state for whether or not the filmstrip is transitioning to
* a displayed state.
*
* @inheritdoc
*/
static getDerivedStateFromProps(props, prevState) {
return {
filmstripBecomingVisible: !prevState.filmstripBecomingVisible
&& props._filmstripVisible
};
}
/**
* Initializes a new {@code Labels} instance.
*
@@ -46,22 +59,6 @@ class Labels extends AbstractLabels<Props, State> {
};
}
/**
* Updates the state for whether or not the filmstrip is being toggled to
* display after having being hidden.
*
* @inheritdoc
* @param {Object} nextProps - The read-only props which this Component will
* receive.
* @returns {void}
*/
componentWillReceiveProps(nextProps) {
this.setState({
filmstripBecomingVisible: nextProps._filmstripVisible
&& !this.props._filmstripVisible
});
}
/**
* Implements React's {@link Component#render()}.
*

View File

@@ -117,17 +117,15 @@ export class LargeVideoBackground extends Component<Props> {
* Starts or stops the interval to update the image displayed in the canvas.
*
* @inheritdoc
* @param {Object} nextProps - The read-only React {@code Component} props
* with which the new instance is to be initialized.
*/
componentWillReceiveProps(nextProps: Props) {
if (this.props.hidden && !nextProps.hidden) {
componentDidUpdate(prevProps: Props) {
if (prevProps.hidden && !this.props.hidden) {
this._clearCanvas();
this._setUpdateCanvasInterval();
}
if ((!this.props.hidden && nextProps.hidden)
|| !nextProps.videoElement) {
if ((!prevProps.hidden && this.props.hidden)
|| !this.props.videoElement) {
this._clearCanvas();
this._clearUpdateCanvasInterval();
}

View File

@@ -109,7 +109,7 @@ class AudioRoutePickerDialog extends Component<Props, State> {
state = {
/**
* Available audio devices, it will be set in
* {@link #componentWillMount()}.
* {@link #componentDidMount()}.
*/
devices: []
};
@@ -132,7 +132,7 @@ class AudioRoutePickerDialog extends Component<Props, State> {
*
* @inheritdoc
*/
componentWillMount() {
componentDidMount() {
AudioMode.getAudioDevices().then(({ devices, selected }) => {
const audioDevices = [];

View File

@@ -54,6 +54,18 @@ const STALE_TIMEOUT = 10 * 1000;
*/
export default class AbstractRecordingLabel
extends Component<Props, State> {
/**
* Implements {@code Component#getDerivedStateFromProps}.
*
* @inheritdoc
*/
static getDerivedStateFromProps(props: Props, prevState: State) {
return {
staleLabel: props._status !== JitsiRecordingConstants.status.OFF
&& prevState.staleLabel ? false : prevState.staleLabel
};
}
/**
* Initializes a new {@code AbstractRecordingLabel} component.
*
@@ -70,12 +82,12 @@ export default class AbstractRecordingLabel
}
/**
* Implements {@code Component#componentWillReceiveProps}.
* Implements {@code Component#componentDidUpdate}.
*
* @inheritdoc
*/
componentWillReceiveProps(newProps: Props) {
this._updateStaleStatus(this.props, newProps);
componentDidUpdate(prevProps: Props) {
this._updateStaleStatus(prevProps, this.props);
}
/**
@@ -137,13 +149,8 @@ export default class AbstractRecordingLabel
}
}, STALE_TIMEOUT);
}
} else if (this.state.staleLabel) {
this.setState({
staleLabel: false
});
}
}
}
/**

View File

@@ -33,24 +33,13 @@ export type Props = {
value: string
};
/**
* The state of the component.
*/
type State = {
/**
* The value entered in the field.
*/
value: string
}
/**
* An abstract React Component for entering a key for starting a YouTube live
* stream.
*
* @extends Component
*/
export default class AbstractStreamKeyForm extends Component<Props, State> {
export default class AbstractStreamKeyForm extends Component<Props> {
helpURL: string;
/**
@@ -61,10 +50,6 @@ export default class AbstractStreamKeyForm extends Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = {
value: props.value
};
this.helpURL = (typeof interfaceConfig !== 'undefined'
&& interfaceConfig.LIVE_STREAMING_HELP_LINK)
|| LIVE_STREAMING_HELP_LINK;
@@ -73,17 +58,6 @@ export default class AbstractStreamKeyForm extends Component<Props, State> {
this._onInputChange = this._onInputChange.bind(this);
}
/**
* Implements {@code Component}'s componentWillReceiveProps.
*
* @inheritdoc
*/
componentWillReceiveProps(newProps: Props) {
this.setState({
value: newProps.value
});
}
_onInputChange: Object => void
/**
@@ -99,9 +73,6 @@ export default class AbstractStreamKeyForm extends Component<Props, State> {
_onInputChange(change) {
const value = typeof change === 'object' ? change.target.value : change;
this.setState({
value
});
this.props.onChange(value);
}
}

View File

@@ -50,7 +50,7 @@ class StreamKeyForm extends AbstractStreamKeyForm {
onChangeText = { this._onInputChange }
placeholder = { t('liveStreaming.enterStreamKey') }
style = { styles.streamKeyInput }
value = { this.state.value } />
value = { this.props.value } />
<TouchableOpacity
onPress = { this._onOpenHelp }
style = { styles.streamKeyHelp } >

View File

@@ -37,26 +37,6 @@ class LiveStreamButton extends AbstractLiveStreamButton<Props> {
iconName = 'icon-public';
toggledIconName = 'icon-public';
/**
* Constructor of the component.
*
* @inheritdoc
*/
constructor(props: Props) {
super(props);
this.tooltip = props._liveStreamDisabledTooltipKey;
}
/**
* Implements {@code Component}'s componentWillReceiveProps.
*
* @inheritdoc
*/
componentWillReceiveProps(newProps: Props) {
this.tooltip = newProps._liveStreamDisabledTooltipKey;
}
/**
* Helper function to be implemented by subclasses, which returns
* a React Element to display (a beta tag) at the end of the button.
@@ -76,6 +56,16 @@ class LiveStreamButton extends AbstractLiveStreamButton<Props> {
);
}
/**
* Returns the tooltip that should be displayed when the button is disabled.
*
* @private
* @returns {string}
*/
_getTooltip() {
return this.props._liveStreamDisabledTooltipKey || '';
}
/**
* Helper function to be implemented by subclasses, which must return a
* boolean value indicating if this button is disabled or not.

View File

@@ -51,7 +51,7 @@ class StreamKeyForm extends AbstractStreamKeyForm {
placeholder = { t('liveStreaming.enterStreamKey') }
shouldFitContainer = { true }
type = 'text'
value = { this.state.value } />
value = { this.props.value } />
{ this.helpURL
? <div className = 'form-footer'>
<a

View File

@@ -36,23 +36,13 @@ class RecordButton extends AbstractRecordButton<Props> {
toggledIconName = 'icon-camera-take-picture';
/**
* Constructor of the component.
* Returns the tooltip that should be displayed when the button is disabled.
*
* @inheritdoc
* @private
* @returns {string}
*/
constructor(props: Props) {
super(props);
this.tooltip = props._fileRecordingsDisabledTooltipKey;
}
/**
* Implements {@code Component}'s componentWillReceiveProps.
*
* @inheritdoc
*/
componentWillReceiveProps(newProps: Props) {
this.tooltip = newProps._fileRecordingsDisabledTooltipKey;
_getTooltip() {
return this.tooltip || '';
}
/**

View File

@@ -262,13 +262,15 @@ export default class DeviceSelectionPopup {
<AtlasKitThemeProvider mode = 'dark'>
<DialogWithTabs
closeDialog = { this.close }
cssClassName = 'settings-dialog'
onSubmit = { onSubmit }
tabs = { [ {
component: DeviceSelection,
label: 'settings.devices',
props: this._dialogProps,
submit: this._onSubmit
} ] } />
} ] }
titleKey = 'settings.title' />
</AtlasKitThemeProvider>
</I18nextProvider>,
document.getElementById('react'));

View File

@@ -91,11 +91,13 @@ class SettingsDialog extends Component<Props> {
return (
<DialogWithTabs
closeDialog = { this._closeDialog }
cssClassName = 'settings-dialog'
defaultTab = {
defaultTabIdx === -1 ? undefined : defaultTabIdx
}
onSubmit = { onSubmit }
tabs = { tabs } />
tabs = { tabs }
titleKey = 'settings.title' />
);
}

View File

@@ -12,7 +12,7 @@ import SpeakerStatsLabels from './SpeakerStatsLabels';
declare var interfaceConfig: Object;
/**
* The type of the React {@code Component} props of {@link SpeakerStats}
* The type of the React {@code Component} props of {@link SpeakerStats}.
*/
type Props = {
@@ -33,7 +33,7 @@ type Props = {
};
/**
* The type of the React {@code Component} state of {@link SpeakerStats}
* The type of the React {@code Component} state of {@link SpeakerStats}.
*/
type State = {
@@ -49,10 +49,6 @@ type State = {
* @extends Component
*/
class SpeakerStats extends Component<Props, State> {
state = {
stats: {}
};
_updateInterval: IntervalID;
/**
@@ -64,19 +60,20 @@ class SpeakerStats extends Component<Props, State> {
constructor(props) {
super(props);
this.state = {
stats: this.props.conference.getSpeakerStats()
};
// Bind event handlers so they are only bound once per instance.
this._updateStats = this._updateStats.bind(this);
}
/**
* Immediately request for updated speaker stats and begin
* polling for speaker stats updates.
* Begin polling for speaker stats updates.
*
* @inheritdoc
* @returns {void}
*/
componentWillMount() {
this._updateStats();
componentDidMount() {
this._updateInterval = setInterval(this._updateStats, 1000);
}

View File

@@ -280,15 +280,15 @@ class Toolbox extends Component<Props> {
*
* @inheritdoc
*/
componentWillReceiveProps(nextProps) {
componentDidUpdate(prevProps) {
// Ensure the dialog is closed when the toolbox becomes hidden.
if (this.props._overflowMenuVisible && !nextProps._visible) {
if (prevProps._overflowMenuVisible && !this.props._visible) {
this._onSetOverflowVisible(false);
}
if (this.props._overflowMenuVisible
&& !this.props._dialog
&& nextProps._dialog) {
if (prevProps._overflowMenuVisible
&& !prevProps._dialog
&& this.props._dialog) {
this._onSetOverflowVisible(false);
this.props.dispatch(setToolbarHovered(false));
}

View File

@@ -37,6 +37,17 @@ type Props = {
export class AbstractWelcomePage extends Component<Props, *> {
_mounted: ?boolean;
/**
* Implements React's {@link Component#getDerivedStateFromProps()}.
*
* @inheritdoc
*/
static getDerivedStateFromProps(props: Props, state: Object) {
return {
room: props._room || state.room
};
}
/**
* Save room name into component's local state.
*
@@ -77,26 +88,15 @@ export class AbstractWelcomePage extends Component<Props, *> {
}
/**
* Implements React's {@link Component#componentWillMount()}. Invoked
* immediately before mounting occurs.
* Implements React's {@link Component#componentDidMount()}. Invoked
* immediately after mounting occurs.
*
* @inheritdoc
*/
componentWillMount() {
componentDidMount() {
this._mounted = true;
}
/**
* Implements React's {@link Component#componentWillReceiveProps()}. Invoked
* before this mounted component receives new props.
*
* @inheritdoc
* @param {Props} nextProps - New props component will receive.
*/
componentWillReceiveProps(nextProps: Props) {
this.setState({ room: nextProps._room });
}
/**
* Implements React's {@link Component#componentWillUnmount()}. Invoked
* immediately before this component is unmounted and destroyed.

View File

@@ -29,7 +29,7 @@ class BlankPage extends Component<Props> {
* @inheritdoc
* @returns {void}
*/
componentWillMount() {
componentDidMount() {
this.props.dispatch(destroyLocalTracks());
}

View File

@@ -57,15 +57,15 @@ class WelcomePage extends AbstractWelcomePage {
}
/**
* Implements React's {@link Component#componentWillMount()}. Invoked
* immediately before mounting occurs. Creates a local video track if none
* Implements React's {@link Component#componentDidMount()}. Invoked
* immediately after mounting occurs. Creates a local video track if none
* is available and the camera permission was already granted.
*
* @inheritdoc
* @returns {void}
*/
componentWillMount() {
super.componentWillMount();
componentDidMount() {
super.componentDidMount();
const { dispatch } = this.props;