diff --git a/react/features/authentication/actionTypes.js b/react/features/authentication/actionTypes.js index cf4c8dacc..9c728a34f 100644 --- a/react/features/authentication/actionTypes.js +++ b/react/features/authentication/actionTypes.js @@ -8,16 +8,6 @@ */ export const CANCEL_LOGIN = Symbol('CANCEL_LOGIN'); -/** - * The type of (redux) action which signals that the {@link WaitForOwnerDialog} - * has been canceled. - * - * { - * type: CANCEL_WAIT_FOR_OWNER - * } - */ -export const CANCEL_WAIT_FOR_OWNER = Symbol('CANCEL_WAIT_FOR_OWNER'); - /** * The type of (redux) action which signals that the cyclic operation of waiting * for conference owner has been aborted. diff --git a/react/features/authentication/actions.js b/react/features/authentication/actions.js index 5636e856a..4dc7439d2 100644 --- a/react/features/authentication/actions.js +++ b/react/features/authentication/actions.js @@ -1,11 +1,11 @@ -/* @flow */ +// @flow +import { appNavigate } from '../app'; import { checkIfCanJoin } from '../base/conference'; import { openDialog } from '../base/dialog'; import { CANCEL_LOGIN, - CANCEL_WAIT_FOR_OWNER, STOP_WAIT_FOR_OWNER, UPGRADE_ROLE_FINISHED, UPGRADE_ROLE_STARTED, @@ -25,7 +25,7 @@ const logger = require('jitsi-meet-logger').getLogger(__filename); * @param {string} password - The XMPP user's password. * @param {JitsiConference} conference - The conference for which the local * participant's role will be upgraded. - * @returns {function({ dispatch: Dispatch, getState: Function })} + * @returns {Function} */ export function authenticateAndUpgradeRole( id: string, @@ -79,13 +79,12 @@ export function cancelLogin() { /** * Cancels {@link WaitForOwnerDialog}. Will navigate back to the welcome page. * - * @returns {{ - * type: CANCEL_WAIT_FOR_OWNER - * }} + * @returns {Function} */ export function cancelWaitForOwner() { - return { - type: CANCEL_WAIT_FOR_OWNER + return (dispatch: Dispatch<*>) => { + dispatch(stopWaitForOwner()); + dispatch(appNavigate(undefined)); }; } @@ -197,7 +196,7 @@ function _upgradeRoleStarted(thenableWithCancel) { * start the process of "waiting for the owner" by periodically trying to join * the room every five seconds. * - * @returns {function({ dispatch: Dispatch })} + * @returns {Function} */ export function waitForOwner() { return (dispatch: Dispatch) => diff --git a/react/features/authentication/middleware.js b/react/features/authentication/middleware.js index f0012a73f..a63a7323b 100644 --- a/react/features/authentication/middleware.js +++ b/react/features/authentication/middleware.js @@ -22,7 +22,6 @@ import { } from './actions'; import { CANCEL_LOGIN, - CANCEL_WAIT_FOR_OWNER, STOP_WAIT_FOR_OWNER, WAIT_FOR_OWNER } from './actionTypes'; @@ -40,8 +39,8 @@ import { LoginDialog, WaitForOwnerDialog } from './components'; MiddlewareRegistry.register(store => next => action => { switch (action.type) { case CANCEL_LOGIN: { - const { thenableWithCancel } - = store.getState()['features/authentication']; + const { dispatch, getState } = store; + const { thenableWithCancel } = getState()['features/authentication']; thenableWithCancel && thenableWithCancel.cancel(); @@ -53,27 +52,18 @@ MiddlewareRegistry.register(store => next => action => { // Instead of hiding show the new one. const result = next(action); - store.dispatch(_openWaitForOwnerDialog()); + dispatch(_openWaitForOwnerDialog()); return result; } // Go back to the app's entry point. _hideLoginDialog(store); - store.dispatch(appNavigate(undefined)); + dispatch(appNavigate(undefined)); } break; } - case CANCEL_WAIT_FOR_OWNER: { - const result = next(action); - - store.dispatch(stopWaitForOwner()); - store.dispatch(appNavigate(undefined)); - - return result; - } - case CONFERENCE_FAILED: if (action.error.name === JitsiConferenceErrors.AUTHENTICATION_REQUIRED) { diff --git a/react/features/base/dialog/components/AbstractDialog.js b/react/features/base/dialog/components/AbstractDialog.js index d610b9e5d..7e85432d5 100644 --- a/react/features/base/dialog/components/AbstractDialog.js +++ b/react/features/base/dialog/components/AbstractDialog.js @@ -71,6 +71,15 @@ export default class AbstractDialog extends Component { this._mounted = false; } + /** + * Dispatches a redux action to hide this dialog. + * + * @returns {*} The return value of {@link hideDialog}. + */ + _hide() { + return this.props.dispatch(hideDialog()); + } + _onCancel: () => void; /** @@ -84,7 +93,7 @@ export default class AbstractDialog extends Component { if ((typeof cancelDisabled === 'undefined' || !cancelDisabled) && (!onCancel || onCancel())) { - this.props.dispatch(hideDialog()); + this._hide(); } } @@ -147,7 +156,7 @@ export default class AbstractDialog extends Component { _onSubmitFulfilled() { this._mounted && this.setState({ submitting: false }); - this.props.dispatch(hideDialog()); + this._hide(); } _onSubmitRejected: () => void; diff --git a/react/features/base/react/components/web/index.js b/react/features/base/react/components/web/index.js index d1f031695..21916624a 100644 --- a/react/features/base/react/components/web/index.js +++ b/react/features/base/react/components/web/index.js @@ -1,4 +1,4 @@ export { default as Container } from './Container'; +export { default as MultiSelectAutocomplete } from './MultiSelectAutocomplete'; export { default as Text } from './Text'; export { default as Watermarks } from './Watermarks'; -export { default as MultiSelectAutocomplete } from './MultiSelectAutocomplete'; diff --git a/react/features/desktop-picker/components/DesktopPicker.js b/react/features/desktop-picker/components/DesktopPicker.js index be297dc93..04ad232af 100644 --- a/react/features/desktop-picker/components/DesktopPicker.js +++ b/react/features/desktop-picker/components/DesktopPicker.js @@ -1,3 +1,5 @@ +// @flow + import Tabs from '@atlaskit/tabs'; import PropTypes from 'prop-types'; import React, { Component } from 'react'; @@ -15,7 +17,12 @@ const THUMBNAIL_SIZE = { }; const UPDATE_INTERVAL = 1000; -const TAB_CONFIGURATIONS = [ +type TabConfiguration = { + defaultSelected?: boolean, + label: string, + type: string +}; +const TAB_CONFIGURATIONS: Array = [ { /** * The indicator which determines whether this tab configuration is @@ -83,6 +90,14 @@ class DesktopPicker extends Component { t: PropTypes.func }; + _poller = null; + + state = { + selectedSource: {}, + tabsToPopulate: [], + typesToFetch: [] + }; + /** * Initializes a new DesktopPicker instance. * @@ -92,13 +107,7 @@ class DesktopPicker extends Component { constructor(props) { super(props); - this.state = { - selectedSource: {}, - tabsToPopulate: [], - typesToFetch: [] - }; - - this._poller = null; + // Bind event handlers so they are only bound once per instance. this._onCloseModal = this._onCloseModal.bind(this); this._onPreviewClick = this._onPreviewClick.bind(this); this._onSubmit = this._onSubmit.bind(this); @@ -175,12 +184,14 @@ class DesktopPicker extends Component { ); } + _onCloseModal: (?string, string) => void; + /** * Dispatches an action to hide the DesktopPicker and invokes the passed in * callback with a selectedSource, if any. * - * @param {string} id - The id of the DesktopCapturerSource to pass into the - * onSourceChoose callback. + * @param {string} [id] - The id of the DesktopCapturerSource to pass into + * the onSourceChoose callback. * @param {string} type - The type of the DesktopCapturerSource to pass into * the onSourceChoose callback. * @returns {void} @@ -190,6 +201,8 @@ class DesktopPicker extends Component { this.props.dispatch(hideDialog()); } + _onPreviewClick: (string, string) => void; + /** * Sets the currently selected DesktopCapturerSource. * @@ -206,6 +219,27 @@ class DesktopPicker extends Component { }); } + /** + * Handles changing of allowed desktop sharing source types. + * + * @param {Array} desktopSharingSourceTypes - The types that will be + * fetched and displayed. + * @returns {void} + */ + _onSourceTypesConfigChanged(desktopSharingSourceTypes = []) { + const tabsToPopulate + = TAB_CONFIGURATIONS.filter(({ type }) => + desktopSharingSourceTypes.includes(type) + && VALID_TYPES.includes(type)); + + this.setState({ + tabsToPopulate, + typesToFetch: tabsToPopulate.map(c => c.type) + }); + } + + _onSubmit: () => void; + /** * Request to close the modal and execute callbacks with the selected source * id. @@ -268,24 +302,7 @@ class DesktopPicker extends Component { this._poller = null; } - /** - * Handles changing of allowed desktop sharing source types. - * - * @param {Array} desktopSharingSourceTypes - The types that will be - * fetched and displayed. - * @returns {void} - */ - _onSourceTypesConfigChanged(desktopSharingSourceTypes = []) { - const tabsToPopulate = TAB_CONFIGURATIONS.filter( - c => desktopSharingSourceTypes.includes(c.type) - && VALID_TYPES.includes(c.type) - ); - - this.setState({ - tabsToPopulate, - typesToFetch: tabsToPopulate.map(c => c.type) - }); - } + _updateSources: () => void; /** * Dispatches an action to get currently available DesktopCapturerSources. diff --git a/react/features/invite/components/AddPeopleDialog.web.js b/react/features/invite/components/AddPeopleDialog.web.js index 11312c962..554be117a 100644 --- a/react/features/invite/components/AddPeopleDialog.web.js +++ b/react/features/invite/components/AddPeopleDialog.web.js @@ -1,3 +1,5 @@ +// @flow + import Avatar from '@atlaskit/avatar'; import InlineMessage from '@atlaskit/inline-message'; import { Immutable } from 'nuclear-js'; @@ -8,8 +10,7 @@ import { connect } from 'react-redux'; import { getInviteURL } from '../../base/connection'; import { Dialog, hideDialog } from '../../base/dialog'; import { translate } from '../../base/i18n'; -import MultiSelectAutocomplete - from '../../base/react/components/web/MultiSelectAutocomplete'; +import { MultiSelectAutocomplete } from '../../base/react'; import { invitePeople, inviteRooms, searchPeople } from '../functions'; @@ -67,6 +68,54 @@ class AddPeopleDialog extends Component { t: PropTypes.func }; + _multiselect = null; + + _resourceClient = { + makeQuery: text => { + const { + _jwt, + _peopleSearchQueryTypes, + _peopleSearchUrl + } = this.props; // eslint-disable-line no-invalid-this + + return ( + searchPeople( + _peopleSearchUrl, + _jwt, + text, + _peopleSearchQueryTypes)); + }, + + parseResults: response => response.map(user => { + return { + content: user.name, + elemBefore: , + item: user, + value: user.id + }; + }) + }; + + state = { + /** + * Indicating that an error occurred when adding people to the call. + */ + addToCallError: false, + + /** + * Indicating that we're currently adding the new people to the + * call. + */ + addToCallInProgress: false, + + /** + * The list of invite items. + */ + inviteItems: new Immutable.List() + }; + /** * Initializes a new {@code AddPeopleDialog} instance. * @@ -76,56 +125,7 @@ class AddPeopleDialog extends Component { constructor(props) { super(props); - this.state = { - /** - * Indicating that an error occurred when adding people to the call. - */ - addToCallError: false, - - /** - * Indicating that we're currently adding the new people to the - * call. - */ - addToCallInProgress: false, - - /** - * The list of invite items. - */ - inviteItems: new Immutable.List() - }; - - this._multiselect = null; - this._resourceClient = { - makeQuery: text => { - const { - _jwt, - _peopleSearchQueryTypes, - _peopleSearchUrl - } = this.props; - - return searchPeople( - _peopleSearchUrl, - _jwt, - text, - _peopleSearchQueryTypes - ); - }, - parseResults: response => response.map(user => { - const avatar = ( // eslint-disable-line no-extra-parens - - ); - - return { - content: user.name, - value: user.id, - elemBefore: avatar, - item: user - }; - }) - }; - + // Bind event handlers so they are only bound once per instance. this._isAddDisabled = this._isAddDisabled.bind(this); this._onSelectionChange = this._onSelectionChange.bind(this); this._onSubmit = this._onSubmit.bind(this); @@ -144,9 +144,9 @@ class AddPeopleDialog extends Component { * invite. */ if (prevState.addToCallError - && !this.state.addToCallInProgress - && !this.state.addToCallError - && this._multiselect) { + && !this.state.addToCallInProgress + && !this.state.addToCallError + && this._multiselect) { this._multiselect.clear(); } } @@ -169,44 +169,22 @@ class AddPeopleDialog extends Component { ); } - /** - * Renders the input form. - * - * @returns {ReactElement} - * @private - */ - _renderUserInputForm() { - const { t } = this.props; - - return ( -
- { this._renderErrorMessage() } - -
- ); - } + _isAddDisabled: () => boolean; /** * Indicates if the Add button should be disabled. * + * @private * @returns {boolean} - True to indicate that the Add button should * be disabled, false otherwise. - * @private */ _isAddDisabled() { return !this.state.inviteItems.length || this.state.addToCallInProgress; } + _onSelectionChange: (Map<*, *>) => void; + /** * Handles a selection change. * @@ -222,6 +200,8 @@ class AddPeopleDialog extends Component { }); } + _onSubmit: () => void; + /** * Handles the submit button action. * @@ -245,27 +225,28 @@ class AddPeopleDialog extends Component { this.props._inviteUrl, this.props._jwt, this.state.inviteItems.filter(i => i.type === 'user')) - .then(() => { - this.setState({ - addToCallInProgress: false - }); + .then( + /* onFulfilled */ () => { + this.setState({ + addToCallInProgress: false + }); - this.props.hideDialog(); - }) - .catch(() => { - this.setState({ - addToCallInProgress: false, - addToCallError: true + this.props.hideDialog(); + }, + /* onRejected */ () => { + this.setState({ + addToCallInProgress: false, + addToCallError: true + }); }); - }); } } /** * Renders the error message if the add doesn't succeed. * - * @returns {ReactElement|null} * @private + * @returns {ReactElement|null} */ _renderErrorMessage() { if (!this.state.addToCallError) { @@ -304,6 +285,34 @@ class AddPeopleDialog extends Component { ); } + /** + * Renders the input form. + * + * @private + * @returns {ReactElement} + */ + _renderUserInputForm() { + const { t } = this.props; + + return ( +
+ { this._renderErrorMessage() } + +
+ ); + } + + _setMultiSelectElement: (Object) => void; + /** * Sets the instance variable for the multi select component * element so it can be accessed directly. @@ -338,13 +347,13 @@ function _mapStateToProps(state) { return { _conference: conference, - _jwt: state['features/jwt'].jwt, - _inviteUrl: getInviteURL(state), _inviteServiceUrl: inviteServiceUrl, + _inviteUrl: getInviteURL(state), + _jwt: state['features/jwt'].jwt, _peopleSearchQueryTypes: peopleSearchQueryTypes, _peopleSearchUrl: peopleSearchUrl }; } -export default translate( - connect(_mapStateToProps, { hideDialog })(AddPeopleDialog)); +export default translate(connect(_mapStateToProps, { hideDialog })( + AddPeopleDialog)); diff --git a/react/features/mobile/callkit/middleware.js b/react/features/mobile/callkit/middleware.js index f0825e720..c1dfc3ef0 100644 --- a/react/features/mobile/callkit/middleware.js +++ b/react/features/mobile/callkit/middleware.js @@ -1,4 +1,4 @@ -/* @flow */ +// @flow import { NativeModules } from 'react-native'; import uuid from 'uuid'; @@ -238,9 +238,9 @@ function _onPerformEndCallAction({ callUUID }) { const conference = getCurrentConference(getState); if (conference && conference.callUUID === callUUID) { - // We arrive here when a call is ended by the system, for - // example when another incoming call is received and the user - // selects "End & Accept". + // We arrive here when a call is ended by the system, for example, when + // another incoming call is received and the user selects "End & + // Accept". delete conference.callUUID; dispatch(appNavigate(undefined)); }