Compare commits

...

37 Commits

Author SHA1 Message Date
virtuacoplenny
e207ad609a Merge pull request #3642 from virtuacoplenny/lenny/tile-view-popover-z
fix(tile-view): popovers should display over icons in other thumbnails
2018-11-30 08:08:21 -08:00
Leonard Kim
35da17f5a6 ref(local-video): merge styles at render
Remove caching of calculated styles, thereby removing
componentWillReceiveProps, by passing in base styles
and passed in styles when rendering.
2018-11-30 08:42:44 +00:00
Guus der Kinderen
fb6949f7ba [Android] Add jsc-android to SDK project / improve build docs
Due to a switch to a newer version of JSCore, the jsc-android dependency is now used by the
SDK. As this dependency is not (yet) available in the Jitsi Maven repository, an error like
this is reported when an application is ran that uses the SDK:

    com.facebook.react.common.JavascriptException: Can't find variable: Symbol

This commit primarily improves the instructions on how to create a local Maven repository
that contains all required dependencies, including the JSCore dependency that was missing.

This intends to address the issue described in https://github.com/jitsi/jitsi-meet/issues/3399
2018-11-29 22:21:27 +00:00
Leonard Kim
9013c0db39 fix(tile-view): popovers should display over icons in other thumbnails 2018-11-29 10:50:25 -08:00
Guus der Kinderen
99542e29e6 [RN] Upgrade React Native to 0.57.6 2018-11-29 10:35:31 +00:00
Saúl Ibarra Corretgé
1ba66e4b65 ios: update CocoaPods dependencies 2018-11-28 16:59:37 +01:00
Saúl Ibarra Corretgé
80afe30e9e feat(chore): make sure all links in package-lock are HTTPS 2018-11-28 16:41:51 +01:00
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
41 changed files with 764 additions and 1230 deletions

View File

@@ -33,80 +33,72 @@ dependencies {
### Build and use your own SDK artifacts/binaries
1. Install all required [dependencies](https://github.com/jitsi/jitsi-meet/blob/master/doc/mobile.md).
Start by making sure that your development environment [is set up correctly](https://github.com/jitsi/jitsi-meet/blob/master/doc/mobile.md).
2. Create the SDK-release assembly, by invoking the following in the jitsi-meet
project source:
A note on dependencies: Apart from the SDK, Jitsi also publishes a binary Maven artifact for some of the SDK dependencies (that are not otherwise publicly available) to the Jitsi Maven repository. When you're planning to use a SDK that is built from source, you'll likely use a version of the source code that is newer (or at least _different_) than the version of the source that was used to create the binary SDK artifact. As a consequence, the dependencies that your project will need, might also be different from those that are published in the Jitsi Maven repository. This might lead to build problems, caused by dependencies that are unavailable.
```bash
cd android/
./gradlew :sdk:assembleRelease
```
When this successfully executes, artifacts/binaries are ready to be published
into a Maven repository of your choice.
If you want to use a SDK that is built from source, you will likely benefit from composing a local Maven repository that contains these dependencies. The text below describes how you create a repository that includes both the SDK as well as these dependencies. For illustration purposes, we'll define the location of this local Maven repository as `/tmp/repo`
3. Configure the Maven repositories in which you are going to publish the
artifacts/binaries during step 4.
In source code form, the Android SDK dependencies are locked/pinned by package.json and package-lock.json of the Jitsi Meet project. To obtain the data, execute NPM in the parent directory:
In the file `android/sdk/build.gradle` modify the line that contains
`"file:${rootProject.projectDir}/../../../jitsi/jitsi-maven-repository/releases"`
$ (cd ..; npm install)
Change this value (which represents the Maven repository location used internally
by the Jitsi Developers) to the location of the repository that you'd like to use.
This will pull in the dependencies in either binary format, or in source code format, somewhere under /node_modules/
4. Publish the Maven artifact/binary of Jitsi Meet SDK for Android in the Maven
repository configured in step 3:
At the time of writing, the React Native dependency is the only one pulled in in binary format. To copy it to your local Maven repository, you can simply copy part of the directory structure that was pulled in by NPM:
```bash
./gradlew :sdk:publish
cd ../
```
5. In _your_ project, add the Maven repository that you configured in step 3, as well
as the dependency `org.jitsi.react:jitsi-meet-sdk` into your `build.gradle`
file. Note that it's needed to pull in the transitive dependencies:
$ cp -r ../node_modules/react-native/android/com /tmp/repo/
```gradle
implementation ('org.jitsi.react:jitsi-meet-sdk:+') { transitive = true }
```
In the same way, copy the JavaScriptCore dependency:
Generally, if you are modifying the JavaScript code of Jitsi Meet SDK for Android only,
the above will suffice. If you would like to publish a third-party react-native module
which Jitsi Meet SDK for Android depends on (and is not publicly available in Maven
repositories) continue below.
$ cp -r ../node_modules/jsc-android/dist/org /tmp/repo/
6. Create the release assembly for _each_ third-party react-native module that you
need, replacing it's name in the example below.
Third-party React Native _modules_, which Jitsi Meet SDK for Android depends on, are download by NPM in source code form. These need to be assembled into Maven artifacts, and then published to your local Maven repository. The SDK project facilitates this.
```bash
./gradlew :react-native-webrtc:assembleRelease
```
To prepare, Configure the Maven repositories in which you are going to publish the SDK artifacts/binaries. In `android/sdk/build.gradle` as well as in `android/build.gradle` modify the lines that contain:
7. Configure the Maven repositories in which you are going to publish the
artifacts/binaries during step 8.
"file:${rootProject.projectDir}/../../../jitsi/jitsi-maven-repository/releases"
In the file `android/build.gradle` (note that this is a different file than the file
that was modified in step 3) modify the line that contains
`"file:${rootProject.projectDir}/../../../jitsi/jitsi-maven-repository/releases"`
Change this value (which represents the Maven repository location used internally by the Jitsi Developers) to the location of the repository that you'd like to use:
Change this value (which represents the Maven repository location used internally
by the Jitsi Developers) to the location of the repository that you'd like to use.
You can use the same repository as the one you configured in step 3 if you want.
"file:/tmp/repo"
8. Publish the Maven artifact/binary of _each_ third-party react-native module that
you need, replacing it's name in the example below. For example, to publish
react-native-webrtc:
Make sure to do this in both files! Each file should require one line to be changed.
```bash
./gradlew :react-native-webrtc:publish
```
To create the release assembly for any _specific_ third-party React Native module that you need, you can execture the following commands, replace the module name in the examples below.
Note that there should not be a need to explicitly add these dependencies in
_your_ project, as they will be pulled in as transitive dependencies of
`jitsi-meet-sdk`.
$ ./gradlew :react-native-webrtc:assembleRelease
$ ./gradlew :react-native-webrtc:publish
You build and publish the SDK itself in the same way:
$ ./gradlew :sdk:assembleRelease
$ ./gradlew :sdk:publish
Alternatively, you can assemble and publish _all_ subprojects, which include the react-native modules, but also the SDK itself, with a single command:
$ ./gradlew assembleRelease publish
You're now ready to use the artifacts. In _your_ project, add the Maven repository that you used above (`/tmp/repo`) into your top-level `build.gradle` file:
allprojects {
repositories {
maven { url "file:/tmp/repo" }
google()
jcenter()
}
}
You can use your local repository to replace the Jitsi repository (`maven { url "https://github.com/jitsi/jitsi-maven-repository/raw/master/releases" }`) when you published _all_ subprojects. If you didn't do that, you'll have to add both repositories. Make sure your local repository is listed first!
Then, define the dependency `org.jitsi.react:jitsi-meet-sdk` into the `build.gradle` file of your module:
implementation ('org.jitsi.react:jitsi-meet-sdk:+') { transitive = true }
Note that there should not be a need to explicitly add the other dependencies, as they will be pulled in as transitive dependencies of `jitsi-meet-sdk`.
## Using the API
=======
Jitsi Meet SDK is an Android library which embodies the whole Jitsi Meet
experience and makes it reusable by third-party apps.

View File

@@ -24,6 +24,7 @@ dependencies {
implementation "com.android.support:support-v4:${rootProject.ext.supportLibVersion}"
implementation "com.android.support:appcompat-v7:${rootProject.ext.supportLibVersion}"
implementation 'org.webkit:android-jsc:+'
implementation 'com.dropbox.core:dropbox-core-sdk:3.0.8'
api 'com.facebook.react:react-native:+'

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

@@ -44,4 +44,15 @@
#largeVideoContainer {
background-color: $defaultBackground !important;
}
/**
* Thumbnail popover menus can overlap other thumbnails. Setting an auto
* z-index will allow AtlasKit InlineDialog's large z-index to be
* respected and thereby display over elements in other thumbnails,
* specifically the various status icons.
*/
.remotevideomenu,
.videocontainer__toptoolbar {
z-index: auto;
}
}

View File

@@ -22,17 +22,17 @@ PODS:
- "GoogleToolboxForMac/NSString+URLArguments (2.1.4)"
- GTMOAuth2 (1.1.6):
- GTMSessionFetcher (~> 1.1)
- GTMSessionFetcher (1.2.0):
- GTMSessionFetcher/Full (= 1.2.0)
- GTMSessionFetcher/Core (1.2.0)
- GTMSessionFetcher/Full (1.2.0):
- GTMSessionFetcher/Core (= 1.2.0)
- GTMSessionFetcher (1.2.1):
- GTMSessionFetcher/Full (= 1.2.1)
- GTMSessionFetcher/Core (1.2.1)
- GTMSessionFetcher/Full (1.2.1):
- GTMSessionFetcher/Core (= 1.2.1)
- ObjectiveDropboxOfficial (3.9.2)
- React (0.57.1):
- React/Core (= 0.57.1)
- React (0.57.6):
- React/Core (= 0.57.6)
- react-native-background-timer (2.0.0):
- React
- react-native-calendar-events (1.6.2):
- react-native-calendar-events (1.6.3):
- React
- react-native-fast-image (4.0.14):
- FLAnimatedImage
@@ -45,42 +45,42 @@ PODS:
- React
- react-native-webrtc (1.67.1):
- React
- React/Core (0.57.1):
- yoga (= 0.57.1.React)
- React/CxxBridge (0.57.1):
- React/Core (0.57.6):
- yoga (= 0.57.6.React)
- React/CxxBridge (0.57.6):
- Folly (= 2016.10.31.00)
- React/Core
- React/cxxreact
- React/cxxreact (0.57.1):
- React/cxxreact (0.57.6):
- boost-for-react-native (= 1.63.0)
- Folly (= 2016.10.31.00)
- React/jschelpers
- React/jsinspector
- React/DevSupport (0.57.1):
- React/DevSupport (0.57.6):
- React/Core
- React/RCTWebSocket
- React/fishhook (0.57.1)
- React/jschelpers (0.57.1):
- React/fishhook (0.57.6)
- React/jschelpers (0.57.6):
- Folly (= 2016.10.31.00)
- React/PrivateDatabase
- React/jsinspector (0.57.1)
- React/PrivateDatabase (0.57.1)
- React/RCTActionSheet (0.57.1):
- React/jsinspector (0.57.6)
- React/PrivateDatabase (0.57.6)
- React/RCTActionSheet (0.57.6):
- React/Core
- React/RCTAnimation (0.57.1):
- React/RCTAnimation (0.57.6):
- React/Core
- React/RCTBlob (0.57.1):
- React/RCTBlob (0.57.6):
- React/Core
- React/RCTImage (0.57.1):
- React/RCTImage (0.57.6):
- React/Core
- React/RCTNetwork
- React/RCTLinkingIOS (0.57.1):
- React/RCTLinkingIOS (0.57.6):
- React/Core
- React/RCTNetwork (0.57.1):
- React/RCTNetwork (0.57.6):
- React/Core
- React/RCTText (0.57.1):
- React/RCTText (0.57.6):
- React/Core
- React/RCTWebSocket (0.57.1):
- React/RCTWebSocket (0.57.6):
- React/Core
- React/fishhook
- React/RCTBlob
@@ -98,7 +98,7 @@ PODS:
- SDWebImage/GIF (4.4.2):
- FLAnimatedImage (~> 1.0)
- SDWebImage/Core
- yoga (0.57.1.React)
- yoga (0.57.6.React)
DEPENDENCIES:
- DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`)
@@ -176,7 +176,7 @@ SPEC CHECKSUMS:
GoogleSignIn: 11183592dc63e105475c7305a325045ff95e02b7
GoogleToolboxForMac: 91c824d21e85b31c2aae9bb011c5027c9b4e738f
GTMOAuth2: c77fe325e4acd453837e72d91e3b5f13116857b2
GTMSessionFetcher: 0c4baf0a73acd0041bf9f71ea018deedab5ea84e
GTMSessionFetcher: 32aeca0aa144acea523e1c8e053089dec2cb98ca
ObjectiveDropboxOfficial: aa792e0556ceb7b72955fa29a2709072f6e35fd9
React: 1fe0eb13d90b625d94c3b117c274dcfd2e760e11
react-native-background-timer: 63dcbf37dbcf294b5c6c071afcdc661fa06a7594

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.

1072
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -58,10 +58,10 @@
"moment-duration-format": "2.2.2",
"postis": "2.2.0",
"prop-types": "15.6.0",
"react": "16.5.0",
"react": "16.6.1",
"react-dom": "16.5.0",
"react-i18next": "7.13.0",
"react-native": "0.57.1",
"react-native": "0.57.6",
"react-native-background-timer": "2.0.0",
"react-native-calendar-events": "github:wmcmahan/react-native-calendar-events#056807286da610d884fb6b4c8ca187a767b261f7",
"react-native-callstats": "3.53.4",
@@ -109,7 +109,7 @@
"file-loader": "1.1.5",
"flow-bin": "0.78.0",
"imports-loader": "0.7.1",
"metro-react-native-babel-preset": "0.47.0",
"metro-react-native-babel-preset": "0.49.2",
"node-sass": "4.10.0",
"precommit-hook": "3.0.0",
"string-replace-loader": "1.3.0",

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

@@ -33,69 +33,11 @@ type Props = {
style: Object
};
/**
* The type of the React {@code Component} state of
* {@link LocalVideoTrackUnderlay}.
*/
type State = {
/**
* The style of {@code LocalVideoTrackUnderlay} which is a combination
* of its default style and the consumer-specified style.
*/
style: Object
};
/**
* Implements a React {@code Component} which underlays the local video track,
* if any, underneath its children.
*/
class LocalVideoTrackUnderlay extends Component<Props, State> {
/**
* Initializes a new {@code LocalVideoTrackUnderlay} instance.
*
* @param {Object} props - The read-only React {@code Component} props with
* which the new instance is to be initialized.
*/
constructor(props) {
super(props);
this.componentWillReceiveProps(props);
}
/**
* Notifies this mounted React {@code Component} that it will receive new
* props. Forks (in Facebook/React speak) the prop {@code style} because its
* value is to be combined with the default style.
*
* @inheritdoc
* @param {Object} nextProps - The read-only React {@code Component} props
* that this instance will receive.
* @returns {void}
*/
componentWillReceiveProps(nextProps) {
// style
const prevStyle = this.props && this.props.style;
const nextStyle = nextProps && nextProps.style;
const assignState = !this.state;
if (prevStyle !== nextStyle || assignState) {
const nextState = {
style: {
...styles.localVideoTrackUnderlay,
...nextStyle
}
};
if (assignState) {
// eslint-disable-next-line react/no-direct-mutation-state
this.state = nextState;
} else {
this.setState(nextState);
}
}
}
class LocalVideoTrackUnderlay extends Component<Props> {
/**
* Implements React's {@link Component#render()}.
*
@@ -105,7 +47,11 @@ class LocalVideoTrackUnderlay extends Component<Props, State> {
*/
render() {
return (
<View style = { this.state.style }>
<View
style = { [
styles.localVideoTrackUnderlay,
this.props.style
] }>
<VideoTrack videoTrack = { this.props._localVideoTrack } />
<TintedView>
{ this.props.children }

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;